从父进程和子进程关闭管道

问题描述

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>

int main() {
    int p[2];
    pipe(p);
    if (fork() == 0) {
        // child
/*#0*/  close(p[1]);
        int received = -1;
        while (read(p[0],&received,4) != 0) {
            printf("receive integer: %d\n",received);
            received = -1;
        }
        printf("child exit\n");
        exit(0);
    } else {
        // parent
/*#1*/  close(p[0]);

        int sent = 42;
        write(p[1],&sent,4);
/*#2*/  close(p[1]);
        printf("wait for child\n");

        wait(0);
    }

    printf("finished\n");
}

我试图理解 C 中的 forkpipe。这个程序派生了一个子进程,它从父进程接收一个整数,然后在管道关闭退出。执行时打印

wait for child
receive integer: 42
child exit
finished

然而,在移除位置 close(p[1]);#0 后,while 循环卡住了:read 将无限等待来自管道的传入变量,并且永远不会检测到管道已关闭

有人可以向我解释为什么 p[1] 必须由父进程(位置 #2)和子进程(位置 #0关闭

解决方法

这是代码(来自 Linux 手册页),代码底部带有注释。 https://man7.org/linux/man-pages/man2/pipe.2.html

在 /#2/ close(pipefd[1]),评论指出“读者将看到 EOF”。这意味着没有任何东西可以再读入子进程,然后语句“read(p[0],&received,4)”将返回 0。在 Linux 手册页 https://man7.org/linux/man-pages/man2/read.2.html 声明“成功时,返回读取的字节数(零表示文件结束)”

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

   int
   main(int argc,char *argv[])
   {
       int pipefd[2];
       pid_t cpid;
       char buf;

       if (argc != 2) {
           fprintf(stderr,"Usage: %s <string>\n",argv[0]);
           exit(EXIT_FAILURE);
       }

       if (pipe(pipefd) == -1) {
           perror("pipe");
           exit(EXIT_FAILURE);
       }

       cpid = fork();
       if (cpid == -1) {
           perror("fork");
           exit(EXIT_FAILURE);
       }

       if (cpid == 0) {    /* Child reads from pipe */
           close(pipefd[1]);          /* Close unused write end */

           while (read(pipefd[0],&buf,1) > 0)
               write(STDOUT_FILENO,1);

           write(STDOUT_FILENO,"\n",1);
           close(pipefd[0]);
           _exit(EXIT_SUCCESS);

       } else {/* Parent writes argv[1] to pipe */
           close(pipefd[0]);          /* Close unused read end */
           write(pipefd[1],argv[1],strlen(argv[1]));
 /*#2*/    close(pipefd[1]);          /* Reader will see EOF */
           wait(NULL);                /* Wait for child */
           exit(EXIT_SUCCESS);
       }
   }