To understand you will have to disect both constructs. First the construct
if( fork() || fork() )
fork();
or written in another way by unfolding the short-circuit or:
if( !fork() )
if( !fork() )
goto not_taken;
fork();
not_taken:
or without goto
's
if( !fork() ) {
if( fork() )
fork();
}
else
fork();
will five-fold the number of processes. This is because first the original process forks, then in the child as the fork
will return zero it will fork
again (short-circuit or). Then if any of these returned non-zero (that is the parent of the fork) it will fork again - this happens to be done in the parent process and it's child. That is the fork
s in the conditions will be called once and the last fork
will be run once.
Now for the second construct:
if( fork() && fork() )
fork();
or with unfolding short-circuit and:
if( fork() )
if( fork() )
fork();
the process count will four-fold. The reason is that first fork
will be run in the parent process and if that returns non-zero (ie the parent process) the second fork
will be called and if that to returns non-zero the last will. So here all three fork
s are called in the original process - which means that it's called three times.
So first construct will make them five processes and the second will then for each process create three more. That means we will have 5*4
processes.
The reason you don't get consistent newlines is probably most due to printf
buffering. The printf
will only immediately print out a newline, and then write the rest of the data when it terminates, this means that they print the string in two step. We have 20 processes that first prints a newline and then prints "hello world" concurrently - the order between what the different processes prints cannot be guaranteed.
A minor possibility is the very fact that the actual printout aren't required to be atomic. That is one process may write "hell", and then a second which already written "hello " gets to write which may be "world" - which would result in "hellworld" instead.