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

Posted

技术标签:

【中文标题】从父进程和子进程关闭管道【英文标题】:close pipe from parent and child process 【发布时间】:2021-07-31 12:05:28 【问题描述】:
#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)进程关闭吗?

【问题讨论】:

因为它是这样工作的。在关闭管道之前,需要关闭管道的所有写入端。系统不会为您检测这种竞争条件;如果您自己被阻止,这是您需要修复的错误。 顺便说一句,您应该将4 替换为sizeof sentsizeof receivedsizeof(int)(最好是前两个)。 【参考方案1】:

这是代码(来自 Linux 手册页),代码底部带有 cmets。 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, &buf, 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);
       
   

【讨论】:

以上是关于从父进程和子进程关闭管道的主要内容,如果未能解决你的问题,请参考以下文章

Perl 行为差异关闭由 open() 产生的子进程与 IPC::Open3

子进程怎么操作父进程中的变量

节点或shell脚本中有没有办法知道子进程是不是启动了自己的子进程?

C# 中 UI 线程和子进程之间的低延迟 IPC [关闭]

父进程和子进程关系

子进程和线程的区别