Home > OS >  Child process not reading from pipe, unless parent calls printf before dup2
Child process not reading from pipe, unless parent calls printf before dup2

Time:06-11

The code below forks a child process, and redirects stdout to the pipe. The child is supposed to read from the pipe but it's not happening. Strangely, if the parent is made to call printf at least once before calling dup2, things seem to work. I guess that's a luck not to be relied upon... but an explanation would be still great. More importantly, why is the child not able to read?

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

int main()
{
    int fd[2]; 
    pid_t p;
   
    if(pipe(fd) == -1) 
        return -1;
    
    if((p=fork()) == -1)
        return -1;

    if(p==0){
        close(fd[1]);
        dup2(fd[0],0);
        fprintf(stderr,"Child starts\n");
        int x;
        scanf("%d",&x);                   // GETS STUCK HERE
        fprintf(stderr,"Child ends\n");  
        exit(0);
    }

    // printf(" ");        // THIS PRINTF SEEMS TO RESOLVE THE ISSUE?!  

    close(fd[0]);
    dup2(fd[1],1);
    printf("3\n");
    fprintf(stderr, "Parent waiting\n");
    wait(0);
    fprintf(stderr, "Parent ends\n");
}

Lots of questions have been asked on fork and pipe but I could not find an answer to my problem. Apologies if it is a duplicate question.

CodePudding user response:

Parent stdout is not line buffered (because it becomes a pipe). So, the data that printf("3\n"); outputs stays in the stream's buffer and is not flushed to the pipe.

There are two ways to fix this (in the parent):

  1. add setlinebuf(stdout); immediately before that printf
  2. add fflush(stdout); immediately after that printf

UPDATE:

And adding the extra printf() fixes it why? I suppose that at that point, the underlying file descriptor for standard output is still a terminal so it puts the stream into line-buffered mode, and that doesn't change when the underlying file descriptor is changed to a pipe. – Jonathan Leffler

Yes, that is correct. If we change:

printf(" ");

Into:

setlinebuf(stdout);

Then, that also works.

(i.e.) the printf to a real tty [implicitly] set line buffered mode.

The probe/test for output is a tty device is [obviously] deferred until something active is done with the device (e.g. printf)

The dup2 is transparent to the stream so the stream's flags stay the same.

  • Related