Search code examples
clinuxpipeforkwait

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


In this program 2 children process are forked, then one send string to another through pipe. When communication finished, the parent get stuck in waiting the exit of the child that reads from the pipe (waitpid(read_child, NULL, 0);). It works fine without any waitpid (both children processes exit) or just wait for the write_child. Why is that?

#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;
}


Solution

  • TL;DR

    • add close(pipe_fd[1]); just before parent process's waitpid(read_child, NULL, 0); will solve the problem.

    The problem here is that, parent process also holds a reference to the two pipe fds.

    The read blocks until some data available or, when the pipe identified by the fd is closed and the read returns immediately with 0 byte.

    Initially the refcount of the write pipe fd is 2, from the writer child and the parent. The parent block waiting for writer, and then the writer exits, refcount decreases to 1. As the writer has exited, the parent's blocking wait returns, and the parent also exit. Refcount decreases to 0, so the write side of pipe is closed, so the reader's blocking read returns with 0 byte, then the reader exits.

    However if the parent wait for the reader without first releasing its reference to write side of the pipe fd, the pipe will not be closed even if the writer has exited, due to the final reference from the parent. This means, the read of the reader child shall block forever...