Search code examples
cmultiprocessingforkrace-condition

no interleave in output while both parent and child are writing to stdout


I was trying to write code to see how to use fork to create child process. Since the child inherit the file object and descriptor from parent, if both child and parent write to the stdout, there should be interleave in the output if I understand correctly. In the following code I write, I declare two strings in both parent and child, and I write them to stdout. The thing I observe is there is no interleave in the output. Do I miss something?

The code:

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

int main() {
  pid_t return_value;
  pid_t wait_p;
  if ((return_value = fork()) != 0) {
     char pa[15]= "Hi from parent\n";
     char pa_2[18]= "Hello from pariii\n";
     write(1, pa, 15);
     write(1, pa_2, 18);
  } else {
     char c[17] = "Hello from child\n";
     char c_2[14] = "Hi from chiii\n";
     write(1, c , 17);
     write(1, c_2, 14);
  }
  exit(0);
}

The output on my machine (Ubutun 18.04, complie with gcc):

Hi from parent
Hello from pariii
Hello from child
Hi from chiii

I see how the atomic nature of write may lead the process to not interleave. But why the output appear as if the parent execute all write first then the child execute its write? Also, no matter how many times I tried, the parent always appear to write before the child write.


Solution

  • The write system call is atomic; that is, it all happens at once. There isn't a chance for the strings to be interleaved. If you were to make the write call in multiple parts (for example, write the string "Hi from ", and then write the string "parent" or "child", and then write a newline), then you will probably see interleaving. When you write the entire message as a single string, that will never happen.

    Note that higher-level calls, like printf or similar, have more complicated buffering rules and therefore might have different rules.


    In response to the added question about why the two parent lines are always before the two child lines, it isn't exactly luck but it isn't guaranteed. On the same system with the same settings, I would expect that it would be mostly consistent whether it scheduled the parent first or the child first, but other platforms might make the opposite decision.

    As for why it never prints one line from the parent, then one line from the child, then the second line from the parent, I'd guess it has to do with the specifics of the scheduler and writing to a virtual terminal. Context switching from one process to another is expensive; if the write is fast enough the scheduler might be able to tell it shouldn't do that. Perhaps if you wrote to disc instead, and called sync in between writes to make sure the (relatively slow) disc was actually involved, it would be more likely to interleave. Or perhaps not; it's really hard to predict. If you really want to see interleaving, I'd write at least a handful of kilobytes, a few at a time, from both the parent and the child; that would be close to certain to interleave.