Search code examples
cpipeforkposixchild-process

Child process hangs, even after closing its input


Suppose I have this example code (error checking largely omitted for clarity)

static void c_way() {
    int pipefd[2], ch;
    FILE *rf, *wf;
    pipe(pipefd);
    switch (fork()) {
    case -1:
        /* something went wrong */
        break;
    case 0:
        dup2(pipefd[0], STDIN_FILENO);
        dup2(pipefd[1], STDOUT_FILENO);
        close(pipefd[0]);
        close(pipefd[1]);
        execvp(cat_args[0], cat_args);
        _exit(0);
    }
    rf = fdopen(pipefd[0], "r");
    wf = fdopen(pipefd[1], "w");
    fprintf(wf, "I have %d apples.\n", 5);
    fclose(wf);
    while ((ch = fgetc(rf)) != EOF)
        putchar(ch);
    puts("Done reading.");
    fflush(stdout);
    fclose(rf);
}

where cat_args is simply {"cat", "-", NULL}. For some reason, despite having closed the parent process's write end of the pipe, EOF never seems to be reached in the fgetc loop, as if the child process was waiting for more input. Did I forget to close some file descriptors? Even without using file pointers (i.e. raw POSIX reads and writes), it still hangs.

I see some similar answered questions, so this is probably a duplicate.


Solution

  • When I run this code I see:

    [notroot]$ ./c_way 
    I have 5 apples.
    

    and then a hang.

    It's a race: your parent process reads its own just-written data from the pipe (and writes it to stdout) before the child process exec's cat.

    The child reads nothing — the pipe has been drained by the parent — and thus blocks patiently on stdin awaiting input. The parent, meanwhile, blocks patiently on the read end of the pipe, awaiting input which will never arrive.

    You want two pipes: one to connect parent to child stdin, and another to connect child stdout to parent. (See also socketpair.)