0

When I run this code, it prints "hello world" 20 times. As per my understanding it should be print it 32 times. Also, the newline gets printed differently with each run. I don't understand this. Please help.

int
main(int argc, char** argv){
    if(fork() || fork())
        fork();
    if(fork() && fork())
        fork();
    printf("\nhello world");
    return 0;
}
toothie
  • 1,019
  • 4
  • 20
  • 47
  • 1
    This is a perfect example of why you shouldn't be using fork() to begin with, it is 1980s programming. Use threads, or if you really must create another process, then create a _separate_ program, with it's own source code and start up that program as a different process with the appropriate API call. – Lundin Feb 18 '16 at 07:21
  • I am not using it for some project or stuff. I am just trying to understand the working of fork() :) – toothie Feb 18 '16 at 07:24
  • 2
    [Short circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation) will affect the outcome. Explaining the exact count is waste of time since no sensible person would write code like this. – user3386109 Feb 18 '16 at 07:26
  • 2
    @Lundin Almost any function can be used incorrectly and that should not be taken for a reason not to use that function. There are perfect valid use cases for `fork` - the only thing you need to make sure when using a function is that you really understand it which may be why the question was asked. – skyking Feb 18 '16 at 07:36
  • 1
    The code is wrong, because every call to `fork` should [keep and test](http://stackoverflow.com/a/18549451/841108) its result. Once you have converted your code to do that, it would be much clearer. – Basile Starynkevitch Feb 18 '16 at 07:40
  • 3
    @Lundin it's flat out silly to say that one shouldn't use `fork` because it's "1980s programming." I have no idea what that means, but `fork`, _when used correctly_, is the _correct_ API to startup a different process on most UNIX-derived systems. It can be followed by some variant of [`exec`](http://man7.org/linux/man-pages/man2/execve.2.html) if you need the new process to use some other executable. So let's be clear. The problem the OP has isn't `fork`. It's not using the API properly. – Nik Bougalis Feb 18 '16 at 07:48
  • @NikBougalis "1980s programming" was a polite way of saying incredibly poor API design, obviously made by the kind of 80s programmers that got all excited by obfuscation and complexity. To create a process which contains all the needless overhead of its parent and to use the same source code for two unrelated purposes is "flat out silly". Anyone with a hint of common sense would agree. `fork()` is a remain from an era long before multi-threading was invented. I'm hardly a Unix guru, but I believe the correct way to start a new process nowadays would be `posix_spawn` and similar sane functions. – Lundin Feb 18 '16 at 08:44
  • It's madcademia code:( – Martin James Feb 18 '16 at 10:22
  • @MartinJames Whats madcademia? – toothie Feb 18 '16 at 12:15
  • @toothie it's a portmanteau word from 'mad academia' :) – Martin James Feb 18 '16 at 16:17

3 Answers3

4

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 forks 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 forks 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.

skyking
  • 13,817
  • 1
  • 35
  • 57
0

Let's count how many times each fork is called. In this block there are 3 forks:

if(fork() || fork())
    fork();

The 1st fork is called 1 time, by the first process.

The 2nd fork is called only 1 time, by the second process. (first process doesn't reach it)

The 3rd fork is called 2 times, by both first and second processes.

So, at this point you already have 5 processes.

For this block there are another 3 forks:

if(fork() && fork())
    fork();

The 1st fork is called 1x 5 times. Here, the forked child process goes to printf directly.

The 2nd fork is called 1x 5 times. Still, the forked child process goes to printf.

The 3rd fork is called 1x 5 times.

So you have 20 in total.

cshu
  • 5,654
  • 28
  • 44
0

This is much easier if you evaluate the two if constructs separately. Count the number of processes created in the first and multiply it by the number created in the second. The result is the total number of processes and the times the printf call is called.

(We are assuming fork calls will not fail.)

The first if statement is shows by this handy chart, where columns are forks, rows are processes. forks - f1, f2, f3
processes - c0, c1, ... ,c4
xN - marks the creation of a process where N is the index of the created process
. - marks the process creation
_ - process exists and does nothing
(no character means the process doesn't exist at that point)

        f1  f2  f3
c0  _   x1  _   x2  _
c1      .   x3  x4  _
c2              .   _
c3          .   _   _
c4              .   _

At this point 5 processes exist, 4 additional were created. The next chart shows the second if statement, but only for one process:

        f1  f2  f3  
c0  _   x1  x2  x3  _   
c1      .   _   _   _
c2          .   _   _
c3              .   _

This if statement, creates 3 additional processes, so 4 exist. But remember we had 5 processes before, not just one. So both if statements together create 5 x 4 processes, 20 in total.

2501
  • 25,460
  • 4
  • 47
  • 87