Search code examples
cforkstdoutstdin

STDIN, STDOUT redirection with printf scanf hangs


Can anyone find out why this code hang at fgets() for parent and scanf() for child?
If I turn child process's printf/scanf into write/read it doesn't hang.
Can any one give a reason to this problem?

int main(){
    int pfd1[2], pfd2[2];
    pipe(pfd1); pipe(pfd2);

    if (fork() == 0){
        dup2(pfd1[1], STDOUT_FILENO);
        dup2(pfd2[0], STDIN_FILENO);
        close(pfd1[0]); close(pfd1[1]);
        close(pfd2[1]); close(pfd2[0]);
        char buf[] = "Hello world\n";
        int n;
        printf("%s", buf);
        fflush(stdin);
        scanf("%d", &n);
        fprintf(stderr, "get n = %d\n", n);
    }    
    else {
        char buf[128];
        FILE *fp_w = fdopen(pfd2[1], "w");
        FILE *fp_r = fdopen(pfd1[0], "r");
        fgets(buf, 128, fp_r);
        printf("%s", buf);
        fprintf(fp_w, "%d\n", 10);

    }

    return 0;
}

Solution

  • Output to stdout is by default line-buffered, meaning the stdout internal buffer is flushed when you print a newline ('\n'). But that is only the default, when stdout is connected to a terminal or console.

    Since you make stdout write to a pipe, it's no longer line-buffered, but fully buffered. That means the output will only be flushed when you fill up the internal buffer, or when explicitly doing by calling fflush(stdout).

    Since your code as shown in the question doesn't flush the buffers, nothing will ever be written, which means the parent process will wait forever on input from fp_r which never comes. Since the parent process is locked, it will in turn not write to fp_w and the reading in the child-process will then also be blocked.

    The solution, as you have discovered, is to explicitly flush stdout after writing to it in the child-process.