在第二次读取时挂在子进程中的管道

时间:2017-10-15 19:46:09

标签: c pipe fork

我正在进行一项任务,我必须创建一个链接列表,该列表由通过称为节点的管道进行通信的进程组成。该程序必须从根进程和一个名为Node 1的子进程开始。用户有四个选项,我坚持使用选项号1,用户应该能够添加节点(进程)。

根进程应该是唯一要求用户输入的进程。其余的应该循环,直到它到达读取块并等待写入。

当用户输入1时,程序检查当前进程是否是最后一个节点(在本例中为节点1)。如果不是,则进程通过在每个fork之前创建的管道将输入写入下一个节点。如果是,则该过程重置isLast变量并创建新管道和分叉。子进程(节点2)然后循环并在读取时卡住,父进程跟随它的脚步。然后Root再次请求使用输入。

问题是该程序似乎没有经历过一次创建节点2的麻烦,但第二次,即使根在“添加节点”中写入节点,节点1也会挂起

非常感谢任何帮助!

#include <stdio.h>
#include <unistd.h>

int main(){
    int isLast = 0;
    int originPID = getpid();
    int input = 0;
    int node;
    int p[2];

    //Node 1
    printf("%s\n", "Forking");
    pipe(p);
    int pid = fork();

    //Set Node 1 to last
    if(pid == 0){
        isLast = 1;
        node = 1;
    }

    while(input != 4){
        //Node Read Block
        if(getpid() != originPID){
            printf("stuck\n");
            read(p[0], &input, sizeof(input));
            printf("free\n");
        }

        //MENU (Root Only)
        if(getpid() == originPID){      
            sleep(1);
            printf("%s", "User Options: Enter a number \n1. Add Node\n2. List Processes\n3. Remove Node\n4. Quit\n");
            scanf("%d", &input);
        }

        //(1) Add Node
        if(input == 1){
            //Checks if last. 
            if(isLast == 1){
                isLast = 0;     //Reset isLast

                //Create pipe and new process
                printf("%s\n", "Forking");
                pipe(p);
                pid = fork();
                if(pid == 0){       
                    isLast = 1;
                    node++;      //Label Node
                }
            }
            //Write to next node
            else{       
                write(p[1], &input, sizeof(input));     
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

当最后一个节点从管道中读取1时,它必须创建一个新管道,通过该管道与要创建的新节点进行通信。我认为这就是它调用pipe(p)的原因。但这是一个问题,因为p[0]包含其唯一的文件句柄副本,从中读取自己的输入。当它下次尝试读取输入时,它将尝试从它为下一个节点设置的管道中读取 - 它已经有效地将其自身与其前一个节点断开连接。

pipe()写入其文件句柄的数组没有什么神奇之处。句柄只是整数。因此,一个简单的解决方案是让孩子制作和使用它从父节点继承的读取文件描述符的副本,而不是引用存储在数组元素中的值。

此外,您应该确保在fork之后,父级和子级各自关闭了他们不会使用的管道末尾的副本。 (Parent关闭读取结束; child关闭写入结束。)您可能无法使程序正常工作,但至少它会泄漏文件描述符。在某些情况下,保留文件描述符的额外副本会导致程序挂起。