-1

I am learning the fork function in C, and was studying for the past exams and came across one question which is quite interesting.

See the code below:

int main(void) {

printf("Hello ");
fork();
printf("Hello ");
fork();
printf("Hello \n");

pause();
return 0;
}

The result output is

Hello Hello Hello 
Hello Hello Hello 
Hello Hello Hello 
Hello Hello Hello 

If I alter my code to

    fork();
    fork();
    printf("Hello ");
    printf("Hello ");
    printf("Hello \n");

The output result is still the same. Why the location of the fork function has no effect on the output itself. Does fork waits for all the printf() to finish before executing? That should explain the output, because the first time printf() will print out the first line of Hello, then it will be forked and 1 more lines of Hello will be printed. Then the 2nd fork will fork the previous 2 lines and produce the 2 more line of Hello.

However, the question alters the code just by a bit and the whole output changes dramatically.

printf("Hello \n");
fork();
printf("Hello ");
fork();
printf("Hello \n");

This results in the output being

Hello 
Hello Hello 
Hello Hello 
Hello Hello 
Hello Hello

I do not understand why just adding a '\n' will result in 5 lines of output instead of 4. This time, if I change the location of the fork function to the top like before, the output changes too. It seems that the fork function had no effect on the first line of printf this time. Can someone explain to me how exactly does fork function outputs stuff?

Benjamin
  • 13
  • 2

2 Answers2

4

The problem is not the order, which is irrelevant since we're dealing with fork() and you don't have guarantees about concurrency of two simultaneous threads.

The problem is that the program prints an amount of Hello which is not correct according to the semantics of the program. If you think about it

  • first printf is executed only by parent process
  • second printf is executed by parent process and child process
  • third printf is executed by parent process, child process, and two respective children of parent and first child

This yields a total amount of 7 printf, while you obtain 12 or 11. The problem resides in the fact that the output is buffered on a line basis, when you invoke a fork() with something inside the stdout buffer then weird things happen. If you change your code to:

#include <unistd.h>

int main(void) {
  printf("(1:%u)\n", getpid());
  fork();
  printf("(2:%u)\n", getpid());
  fork();
  printf("(3:%u)\n", getpid());
  pause();
  return 0;
}

You will obtain:

cuboid:Dev jack$ ./a.out 
(1:4307)
(2:4307)
(3:4307)
(2:4308)
(3:4309)
(3:4308)
(3:4310)

Which is correct since now we're forcing a flush (with \n) on each call of printf, and the pid corresponds to what we saw before: 1, 2, 4 calls.

If you force flush before calling fork() in your code, eg:

int main(void) {
  printf("1Hello ");
  fflush(stdout);
  fork();
  printf("2Hello ");
  fflush(stdout);
  fork();
  printf("3Hello \n");
  pause();
  return 0;
}

You will obtain:

cuboid:Dev jack$ ./a.out 
1Hello 2Hello 3Hello 
2Hello 3Hello 
3Hello 
3Hello 

Which yields the same correct result, and you can see that parent process pass through all 3 lines, first child only through 2nd and 3rd while last two children only through last one.

Jack
  • 131,802
  • 30
  • 241
  • 343
2

You've run into a peculiar side effect of buffered I/O using printf(). Normally, printf() will only actually print the text to the console if there is a newline, otherwise it will buffer that text internally waiting for the next newline.

What has happened in your case is that the the first two printf() calls did not go to the console but stayed buffered. When you forked your program, that internal buffer was forked as well - meaning that its contents were copied into the new program as well. Thus all copies of the program had all three copies of "hello" when they did the final printf().

If instead you forced the text out to the console with fflush(0) you should get the behaviour you expected (sort of):

int main(void) {
    printf("Hello ");   fflush(0);
    fork();
    printf("Hello ");   fflush(0);
    fork();
    printf("Hello \n"); fflush(0);
    pause();
    return 0;
}

leads to

Hello Hello Hello 
Hello Hello 
Hello 
Hello 

or

Hello Hello Hello Hello 
Hello 
Hello 
Hello 

you could get other combinations as well...

You still have a race between the different copies of your program to see who finishes first and prints the newline relative to the others.

Siyual
  • 16,415
  • 8
  • 44
  • 58
N. Leavy
  • 1,004
  • 9
  • 13