Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explanation of a output of a C program involving fork()

Tags:

c

fork

Running this program is printing "forked!" 7 times. Can someone explain how "forked!" is being printed 7 times?

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

int main(){

  fork() && fork() || fork() && fork();

  printf("forked!\n");

  return 0;
}
like image 651
sushil Avatar asked Jan 17 '23 08:01

sushil


2 Answers

There're several concepts being used here, first one is knowing what fork does and what it returns in certain circumstances. Shortly, when it gets called, it creates a duplicate process of the caller and returns 0 (false for logical expressions) in child process and non-zero (true for logical expressions) for parent process. Actually, it could return a negative (non-zero) value in case of an error, but here we assume that it always succeeds.

The second concept is short-circuit computation of logical expressions, such as && and ||, specifically, 0 && fork() will not call fork(), because if the first operand is false (zero), then there's no need to compute the second one. Similarly, 1 || fork() will not call fork() neither.

Also note that in child processes the computation of the expression continues at the same point as in the parent process.

Also, note that the expression is computed in the following order due to precedence:

(fork() && fork()) || (fork() && fork())

These observations should lead you to the correct answer.

Consider the simplified example of fork() && fork()

   fork()        
  /     \
false   true && fork()
                /   \
             false  true

So here we have three processes created, two of which return false as the result and one returning true. Then for || we have all the processes returning false trying to run the same statement again, so we have 2 * 3 + 1 = 7 as the answer.

like image 111
unkulunkulu Avatar answered Jan 30 '23 10:01

unkulunkulu


Unlike I stated in my comment, this is not about buffering. The process forks. The parent executes the 2nd fork, while the child short circuits the second and executes the 3rd. The first grandchild evaluates the first && as false and executes the 3rd fork. That spawns 2 processes, one of which evaluates the 4th fork. Meanwhile (or before, or after...there's a race condition!), the other child has become 3 processes while evaluating the RHS of the ||. In all, 7 processes end up running. Draw a tree.

To simplify the calculation, consider:

int f( void )
{
  int k; 
  k = fork();
  printf( "%d\n", (int) getpid());
  fflush( stdout );
  return k;
}

int main( void ) { f() && f() || f() && f(); return 0; } 
like image 43
William Pursell Avatar answered Jan 30 '23 08:01

William Pursell