3

Simple piece of code:

#include <stdio.h>
#include <string.h>

main()
{
     printf("Process");
     fork();
     fork();
     return 0;
}

From my understanding of fork(), after this code executes we will have 3 child processes and 1 parent process. Also whenever we call fork() the execution should start from the statement immediately after the fork() statement. Hence according to me "Process" should be printed only once. But in my output Process is being printed 4 times. How is that possible?

bigbong
  • 541
  • 4
  • 12

2 Answers2

10

Because the standard output is line buffered by default, when you call fork(), the output buffer is inherited by all the children processes.

There are several different ways to change this behavior:

Add a new line character at the end:

printf("Process\n");

or call fflush() to flush the output:

printf("Process");
fflush(stdout);

or change standard output to not buffered using setbuf() or setvbuf():

setbuf(stdout, NULL);
printf("Process");

Using either way, you'll see the output only once.

Note: see @Dvaid Schwartz's answer for the bug with calling atexit() multiple times in your code.

Community
  • 1
  • 1
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 4
    This doesn't actually fix the bug. It just changes the code from "having a horrible bug that causes it do the wrong thing" to "having a horrible bug that happens to cause no harm by luck". It explains his results, but not the underlying problem (that atexit handlers run four times) nor does it tell him how to fix the underlying problem. – David Schwartz Feb 25 '14 at 08:42
4

Your program has a bug. All the children return from main, causing atexit handlers to run four times. The children should call _exit.

Here's how your code should look:

#include <stdio.h> 
#include <string.h>
#include <unistd.h>

main()
{
     int is_child = 0; 
     printf("Process");

     if (fork() == 0) 
        is_child = 1; 

     if (fork() == 0) 
        is_child = 1;

     if (is_child)
        _exit(0);

     return 0;
}
David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • And if child needs to print something to stdout, it ... – user3125367 Feb 25 '14 at 08:50
  • 2
    Okay I see. What kind of adverse effects can this sort of bug cause? – bigbong Feb 25 '14 at 08:50
  • Can you explain why repeated invocation of such handlers is such a bad thing? – Lightness Races in Orbit Feb 25 '14 at 09:01
  • @user3125367 Ask your own question. I'll be happy to explain how to do that correctly. It requires the parent's cooperation. What you can do after a `fork` and before an `exec` is limited and must be carefully orchestrated because you share some resources with the parent. – David Schwartz Feb 25 '14 at 09:07
  • @LightnessRacesinOrbit This program is as good an example as any. But imagine a program that writes to a file or network connection. Duplicate invocation of an atexit handler can result in the data being written twice. – David Schwartz Feb 25 '14 at 09:08
  • @bigbong You never know. It depends what the atexit handlers do. It may work just fine until some library changes somewhere else completely and then horrible things happen. – David Schwartz Feb 25 '14 at 09:09
  • 2
    Please extend this answer, if possible. – user3125367 Feb 25 '14 at 09:09
  • From docs :"The function _exit() is like exit(3), but does not call any functions registered with atexit(3) or on_exit(3). Whether it flushes standard I/O buffers and removes temporary files created with tmpfile(3) is implementation-dependent." As it is implementation-dependent whether it flushes I/O buffers, does it mean the parent still has to call flush() to avoid writing twice ? – Dabo Feb 25 '14 at 09:12
  • @Dabo ["Open streams shall not be flushed."](http://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html) – David Schwartz Feb 25 '14 at 09:16
  • @David Okay so it's a general risk and not something specific to the handler mechanism itself. – Lightness Races in Orbit Feb 25 '14 at 09:19
  • @LightnessRacesinOrbit The basic idea is that returning from `main` or calling `exit` in a child causes the library to perform the on-termination operations for the original process, even though it hasn't terminated. That can do any amount of damage, depending on what those termination behaviors are. It's a bug. – David Schwartz Feb 25 '14 at 09:22
  • See this is why I vastly prefer spawning threads to forking into child processes. Of course that's not always appropriate. – Lightness Races in Orbit Feb 25 '14 at 09:23