通过管道编写结构数组

时间:2012-10-17 17:49:31

标签: c process struct pipe

我正在学习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结构,从头到尾处理数组中的数字(在那里指定)并将计算的统计数据保存到统计结构中。

我遇到的代码最麻烦的部分如下(我正在添加注释而不是其他代码来解释做什么就好了):

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将出现在孩子的前面,所以我认为它不会等待孩子写入管道。无论如何,没有它就行不通。

或许结构方法可能不是很好?

非常感谢你!

2 个答案:

答案 0 :(得分:3)

您需要在分叉之前创建管道。否则你要创建两个管道。一个在你父母身上,一个在你孩子身上没有连接。

对于孩子获得所有数据后wait,否则如果孩子在write阻止管道缓冲区清除,则可能会永远等待。 wait释放与死去的孩子相关的资源。如果你的孩子没有死,这将等到它。

另一件事:使用fwrite将数据块(结构数组)写入文件描述符。 fread阅读。您可以使用FILE*将文件描述符转换为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;
}

答案 1 :(得分:0)

对于被终止的孩子,执行wait()允许系统释放与孩子相关的资源;如果没有执行wait(),那么被终止的孩子将继续留在&#34; zombie&#34;州。它是一种安全的做法,总是很好用。参考: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

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

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

使用pipe()(在父级中创建管道然后分叉)修复逻辑问题后,您的代码应该按预期工作。管道不应该适用于任何数据类型或您编写的结构。