Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duplicated output using printf() and fork() in C

Tags:

c

linux

fork

I run this small program to test fork(), and I can't figure out the output, the program's code is:

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

int i = 0;
void create()
{
    fork();
    printf("Inside i= %d \n", i);
    i = i + 1;
    fork();
}
int main()
{
    create();  
    return 0;
}

The output is:

Inside i= 0 
Inside i= 0 
Inside i= 0 
Inside i= 0 

Aren't there supposed to be just two outputs, because in the last fork(), the children has nothing to print out?

I've read that the process child will execute the next instruction, after the fork(), though the last children seems to have executed the printf() instruction.

like image 867
OussamaMater Avatar asked Jan 22 '20 13:01

OussamaMater


1 Answers

The output of your program is highly implementation dependent.

The C standard library applies buffering to the output stream. This means that it accumulates characters to write until the buffer reaches a certain length (or until a certain condition is met), and then outputs all the text at once.

The most common behavior is to use line buffering, which means that the text will be printed out when a newline character (\n) is encountered. This is indeed what happens on my machine with your example. Since you fork() before the printf(), two processes execute the call and the output is immediately printed since there is a newline:

$ ./prog
Inside i= 0
Inside i= 0

The other fork() is then executed on each of the two processes, but there's nothing more to print out, since the internal output buffer has already been emptied, so nothing noticeable happens in this case.

However, depending on your specific implementation and the conditions in which the program is run, printf() (and in general any stdio function) could decide to apply different buffering rules.

For example, when piping the output to another program or to a file, glibc usually uses a fixed size buffer and does not do line buffering. Since the buffer is not filled with a single short printf(), the text is retained inside it to be printed later. When you fork() a second time, each of the new children gets a copy of said buffer, and all the text is then printed (by each one of them) when the process exits. On my system, when piping, this is the output:

$ ./prog | cat
Inside i= 0
Inside i= 0
Inside i= 0
Inside i= 0

If you want to make sure the text is printed right away, you can either use fflush() or disable buffering of stdout with setvbuf().

Examples:

  • Using fflush():

    void create()
    {
        fork();
        printf("Inside i= %d \n", i);
        fflush(stdout);
        i = i + 1;
        fork();
    }
    
  • Using setvbuf():

    int main()
    {
        setvbuf(stdout, NULL, _IONBF, 0);
        create();  
        return 0;
    }
    
like image 163
Marco Bonelli Avatar answered Nov 19 '22 11:11

Marco Bonelli