2

我正在学习 Linux 中的流程管理,我需要让孩子和父母通过管道进行交流。我已经声明了两个结构:

typedef struct
{
    double min, max, avg;   /*Number stats*/
} Stats;

typedef struct {
    pid_t pid;      /*Process ID*/
    int first;      /*First number to process*/
    int last;       /*Last number to process*/
    int fd[2];      /*Pipe descriptor*/
    Stats stats;    /*Stats computed by process*/
}Process_list;

我需要 M 个子进程从一组数字中计算一些统计数据(划分工作)。然后,子进程将读取 Process_list 结构,从头到尾处理数组中的数字(在此处指定)并将计算的统计信息保存到 stats 结构中。

与我的代码问题最相关的部分如下(我正在添加注释而不是其他代码来解释可以完成的操作):

int main(int argc, char *argv[])
{
    pList=(Process_list *)malloc((M+1)*sizeof(Process_list));
    /*Correct allocation is checked*/

    int i;
    pid_t pid;
    for (i=0 ; i<M ; i++)       /*M clones*/
    {
        /*Fork and pipe creation*/
        pid = fork ();
        pipe(pList[i].fd);

        /*If it's the child*/
        if ( pid == 0 ) 
        {
            pList[i].pid=pid; 
            printf("CHILD %d: %d a %d\n",i, pList[i].first,pList[i].last);

            /*A function here computes stats and saves them OK in the struct*/
            /*(e.g. min is saved in pList[i].stats.max, when printed it's a reasonable value)*/

            /*Now I need to send the info to the parent*/
            ret1=close(pList[i].fd[0]);
            /*ret1=0 => OK */

            ret2=write(pList[i].fd[1], &(pList[i].stats), sizeof(Stats));
            printf("return write: %d\n",ret2);
            /*TROUBLE HERE! This isn't even printed. sizeof(Stats)=24 which I think is OK*/

            exit(EXIT_SUCCESS);
        }

        /*Parent*/
        else if ( pid > 0 )
        {
            wait(NULL); /*Is this really neccesary?*/

            ret1=close(pList[i].fd[1]);
            ret2=read(pList[i].fd[0], &(pList[i].stats), sizeof(Stats));
            /*Both ret1 and ret2 = 0*/

            printf("[p]Mín: %lf\n Max: %lf\nAverage: %lf\n",pList[i].stats.min,pList[i].stats.max,pList[i].stats.avg);
            /*Everything printed here  = 0.0000000000*/


        }

        else /*fork error*/
                return -1;
    }

所以我的问题是孩子们完美地计算了他们的统计数据,但父母没有收到他们。write() 函数什么也不做。这发生在 M 的每一个值上(包括 M=1 - 只有一个进程)。

另外我不知道是否需要等待(NULL),因为我已经看到了一些不使用它的工作示例。但是,如果我不在那里写,则父母的 printfs 将出现在孩子的之前,所以我认为它只是不等待孩子在管道中写入。无论如何,它没有它是行不通的。

或者也许结构方法不是一个好方法?

非常感谢您!

4

2 回答 2

3

您必须在分叉之前创建管道。否则,您将创建两个管道。一个在您的父母身上,一个在您的孩子身上,它们没有连接。

并且对于获得所有数据后的孩子,如果孩子阻塞管道缓冲区清除wait,您可能最终会永远等待。释放与死去的孩子相关的资源。如果您的孩子没有死,这将等到它死了。writewait

另一件事:fwrite用于将数据块(结构数组)写入文件描述符。fread阅读。您可以将文件描述符转换为FILE*with fdopen

int main(int argc, char *argv[])
{
pList=(Process_list *)malloc((M+1)*sizeof(Process_list));
/*Correct allocation is checked*/

int i;
pid_t pid;
for (i=0 ; i<M ; i++)       /*M clones*/
{
    /*Fork and pipe creation*/
    if(pipe(pList[i].fd)) {
        perror("pipe");
        return -1;
    }
    pid = fork ();

    /*If it's the child*/
    if ( pid == 0 ) 
    {
        pList[i].pid=pid; 
        printf("CHILD %d: %d a %d\n",i, pList[i].first,pList[i].last);

        /*A function here computes stats and saves them OK in the struct*/
        /*(e.g. min is saved in pList[i].stats.max, when printed it's a reasonable value)*/

        /*this just closes your read end of the pipe*/
        close(pList[i].fd[0]);
        /*ret1=0 => OK */

        FILE* fp = fdopen(pList[i].fd[1], "w");
        while (!fwrite(&(pList[i].stats), sizeof(Stats), 1, fp) && !feof(fp));
        if (feof(fp)) {
            fclose(fp);
            fprintf(stderr, "reader closed his end of the pipe\n");
            return -1;
        }

        // sends eof to reader
        fclose(fp);

        printf("%d bytes written successfully",sizeof(Stats));
        /*TROUBLE HERE! This isn't even printed. sizeof(Stats)=24 which I think is OK*/

        exit(EXIT_SUCCESS);
    }

    /*Parent*/
    else if ( pid > 0 )
    {

        // this is actually nessesary to ensure that the pipe properly closes
        close(pList[i].fd[1]);
        FILE* fp = fdopen(pList[i].fd[0], "r");

        if (!fread(&(pList[i].stats), sizeof(Stats), 1, fp)) {
            fprintf(stderr, "writer sent eof, but not enough data\n");
            return -1;
        }

        fclose(fp);

        printf("[p]Mín: %lf\n Max: %lf\nAverage: %lf\n",pList[i].stats.min,pList[i].stats.max,pList[i].stats.avg);
        /*Everything printed here  = 0.0000000000*/

        wait(0);

    }

    else /*fork error*/
            return -1;
}
于 2012-10-17T17:59:49.683 回答
0

在终止子的情况下,执行 await()允许系统释放与子关联的资源;如果 await()没有被执行,那么被终止的孩子保持在“僵尸”状态。这是一种安全的做法,而且总是很好用。参考:man -s2 wait

系统wait(NULL)调用意味着,父进程将等待任何子进程(在您的情况下,它将等待刚刚分叉的子进程)并且不会存储您的子进程返回的状态。如果您想更好地控制等待,请查看:man -s2 waitpid. 基本上,wait()waitpid().

现在,管道部分。您正在子进程内创建一个管道。父进程如何访问它?实际上它是这样工作if a pipe is created in a parent process, the pipe fd's will be copied to the child process.的:不是相反。参考:man -s2 pipe

所以,在父进程中创建管道,然后发出a fork(),然后在子进程中使用管道fd发送给父进程。子进程现在可以write()将数据写入管道的 write fd,父代码现在可以写入read()子进程所写的内容。

您编写了一个从子到父的单向 IPC 通信。因此,您正在关闭子级中的读取 fd,并且您也正在关闭父级中的写入 fd。所以,这样你只能在孩子中写作,而只能在父母中阅读。逻辑正确执行。没有错误。

一旦解决了 pipe() 的逻辑问题(在父级中创建管道然后分叉),您的代码应该可以按预期工作。管道没有理由不适用于您编写的任何数据类型或结构。

于 2012-10-17T18:55:48.370 回答