当父级调用 waitpid() 时,分叉进程不会退出

forked process doesn't exit when parent invoke waitpid()

在这个程序中,2 个子进程被 fork,然后一个通过 pipe 向另一个发送字符串。通信完成后,父进程卡在等待从管道读取的子进程退出 (waitpid(read_child, NULL, 0);)。它在没有任何 waitpid(两个子进程都退出)或只是等待 write_child 的情况下工作正常。这是为什么?

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

int main(int argc, char *argv[])
{
    int pipe_fd[2];
    if (pipe(pipe_fd) == -1)
    {
        // fail to build pipe
        perror("pipe");
        exit(EXIT_FAILURE);
    }

    int read_child = fork();
    if (read_child == 0)
    {
        sleep(0.5);
        close(pipe_fd[1]);
        printf("child %d read from pipe:\n", (int)getpid());
        char buffer;
        while (read(pipe_fd[0], &buffer, 1) > 0)
        {
            write(STDOUT_FILENO, &buffer, 1);
        }
        write(STDOUT_FILENO, "\n", 1);

        close(pipe_fd[0]);
        printf("read child exits\n");
        exit(0);
    }

    int write_child = fork();
    if (write_child == 0)
    {
        sleep(0.5);

        close(pipe_fd[0]);
        printf("child %d writes to pipe\n", (int)getpid());

        char message[100];
        sprintf(message, "greeting from brother %d", (int)getpid());
        write(pipe_fd[1], message, strlen(message));

        close(pipe_fd[1]);
        printf("write child exits\n");
        exit(0);
    }

    waitpid(read_child, NULL, 0);
    // waitpid(write_child, NULL, 0);

    return 0;
}

TL;DR

  • 在 parent 进程的 waitpid(read_child, NULL, 0); 之前添加 close(pipe_fd[1]); 将解决问题。

这里的问题是,parent 进程还持有对两个管道 fds 的引用。

read 阻塞直到一些数据可用,或者当 fd 标识的管道关闭并且 read returns 立即为 0 字节时。

最初,写入管道 fd 的引用计数为 2,来自写入器 child 和 parent。 parent阻塞等待writer,然后writer退出,refcount减1。随着writer退出,parent阻塞等待returns,[=34= 】 也退出。 Refcount 减少到 0,所以管道的写端被关闭,所以 reader 用 0 字节阻塞 read returns,然后 reader 退出。

然而,如果 parent 等待 reader 而没有首先释放其对管道 fd 写入端的引用,即使写入器已经退出,管道也不会关闭,因为来自 parent 的最终参考。这意味着,reader child 的 read 将永远阻塞...