cprocesspipefork

Parent process completing before child but still prints out data from pipe


I recreated the example in the man pages for the pipe() system call and am a little confused as to why the 'Hello world' message is printed when the parent process seems to complete before the child process:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
  const int BSIZE = 100;
  int fildes[2];
  char buff[BSIZE];

  pipe(fildes);
  pid_t rc = fork();

  if (rc == 0) {
    printf("in child\n");
    close(fildes[0]);
    write(fildes[1], "Hello world\n", 13);
    close(fildes[1]);
  } else {
    printf("in parent\n");
    close(fildes[1]);
    read(fildes[0], buff, BSIZE);
    close(fildes[0]);
    printf("%s", buff);
  }

  return 0;
}

The output from this looks like:

in parent
in child
Hello world

From the output it looks like the parent process is finishing before the child process runs. If so, how does Hello world print? If read is called before the child process finishes writing i would expect buff to be empty (or at least incomplete) when read is called.

I to remove the close() calls to see if read() was waiting on the other end to close or if the pipe() method waited, but I got the same results.

Is this just a timing thing? It just happens to finish this way and I should be adding a `wait()` call in the parent process?

I didn't the answer in the man page for pipe(), and couldn't phrase it properly for google, so I thought I would try and ask here.

Thanks


Solution

  • The order of two output lines in parent and in child is not defined.

    In the parent process read(fildes[0], buff, BSIZE) is a blocking read of at least 1 byte from the pipe. You can convince yourself of that, for example, by adding a sleep(3) before the child write(fildes[1], "Hello world\n", 13);. The write will be atomic as it's less than PIPE_BUF bytes. This effectively synchronizes the child and parent processes so the parent printf("%s", buff); happens last. The pipe will buffer the data if the client happens to exit before the parent.

    Here's a slightly revised version of your program which shows you that the parent exists after the client:

    #include <stdio.h>
    #include <unistd.h>
    
    #define BSIZE 100
    
    int main() {
        int fildes[2];
        pipe(fildes);
        if (fork()) {
            char buff[BSIZE];
            printf("in parent\n");
            close(fildes[1]);
            read(fildes[0], buff, BSIZE); // blocking
            close(fildes[0]);
            printf("%s", buff);
            printf("out parent\n");
        } else {
            printf("in child\n");
            close(fildes[0]);
            // sleep(3);
            write(fildes[1], "Hello world\n", 13);
            close(fildes[1]);
            printf("out child\n");
        }
    }
    

    which example output:

    in parent
    in child
    out child
    Hello world
    out parent
    

    This suggest the parent exists after the client.