Search code examples
cpipeforkchild-process

What Race Causes the Output to Look Different?


I was given this question on a midterm last year.

Consider the following program.

 #include <stdio.h>
 #include <unistd.h>
 int main (void) {
  int pid = getpid ();
  printf ("hello world (pid:%d)\n", pid);
  int rc = fork();
  if (rc < 0) {
   fprintf (stderr, "fork failed\n");
   retrun 1;
  }
  pid = getpid();
  if (rc == 0) {
   printf ("hello, I am child (pid:%d)\n", pid);
  } else {
   printf("hello, I am parent of %d (pid:%d)\n", rc, pid);
  } 
  return 0;
 }

And consider the following behavior that I got when I compiled and ran this program:

$ gcc -02 -Wall question.c
$ ./a.out # First program run
hello world (pid:20600)
hello, I am parent of 20601 (pid:20600)
hello, I am child (pid:20601)
$ ./a.out | cat # Second program run
hello world (pid:20605)
hello, I am parent of 20607 (pid:20605)
hello world (pid:20605)
hello, I am child (pid:20607)

a) What race could cause the output to look substantially different from either the first or the second run, and what would this output look like?

b) Explain each different in the outputs of the two program runs.

For part (a) I argued that there is a race between the child process and the parent process, and that the child can print before the parent, but apparently that was wrong. Is there any other race that would cause the output to be different? And why is my answer wrong?

For part (b) I am shaky on multiple things. First is that I see the PIDs are different in both runs, but I don't have a good explanation for that. Second that extra hello world in the second run is because of the way the program is being run with a pipe and the cat command?


Solution

  • The problem is that you pipe the output to cat.

    By default, when stdout is connected to a terminal or console, then stdout is line buffered which means that the internal buffers are flushed at newline (or when the buffer is full, or when fflush is called).

    But when stdout is not connected to a terminal or console, like when it is connected to a pipe, it becomes fully buffered. This means it will only be flushed if the buffer becomes full or fflush is called. Printing newlines doesn't do anything special, the newline is just added to the buffer.

    Now because stdout is fully buffered the buffer with the contents of the first printf call will be copied to the child process as part of the fork call, and will be flushed when the child process exits.