Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this program print "forked!" 4 times?

Why does this program print “forked!” 4 times?

#include <stdio.h> #include <unistd.h>  int main(void) {    fork() && (fork() || fork());    printf("forked!\n");   return 0; } 
like image 664
Rawan Lezzeik Avatar asked Nov 03 '14 14:11

Rawan Lezzeik


People also ask

How many processes are created by 4 forks?

Fork #4 is executed by half of the processes created by fork #3 (so, four of them). This creates four additional processes. You now have twelve processes.

Where does a forked process start?

The fork starts from line 3, the point where the fork occurred. Show activity on this post. When fork returns, it returns in both the parent (returning the PID of the child) and the child (returning 0). Execution continues from there in both the parent and the child.

How many times the word process will be printed?

So a total of 4 times "PROCESS" will be printed.

What does fork return in C?

RETURN VALUE Upon successful completion, fork() returns 0 to the child process and returns the process ID of the child process to the parent process. Otherwise, -1 is returned to the parent process, no child process is created, and errno is set to indicate the error.


2 Answers

The one comes from main() and the other three from every fork().

Notice that all three forks() are going to be executed. You might want to take a look at the ref:

RETURN VALUE

Upon successful completion, fork() shall return 0 to the child process and shall return the process ID of the child process to the parent process. Both processes shall continue to execute from the fork() function. Otherwise, -1 shall be returned to the parent process, no child process shall be created, and errno shall be set to indicate the error.

Note that the process id cannot be zero, as stated here.


So what really happens?

We have:

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

So the first fork() will return to the parent its non zero process id, while it will return 0 to the child process. That means that the logic expression's first fork will be evaluated to true in the parent process, while in the child process it will be evaluated to false and, due to Short circuit evaluation, it will not call the remaining two fork()s.

So, now we know that are going to get at least two prints (one from main and one from the 1st fork()).

Now, the 2nd fork() in the parent process is going to be executed, it does and it returns a non-zero value to the parent process and a zero one in the child process.

So now, the parent will not continue execution to the last fork() (due to short circuiting), while the child process will execute the last fork, since the first operand of || is 0.

So that means that we will get two more prints.

As a result, we get four prints in total.


Short circuiting

Here, short circuiting basically means that if the first operand of && is zero, then the other operand(s) is/are not evaluated. On the same logic, if an operand of a || is 1, then the rest of the operands do not need evaluation. This happens because the rest of the operands cannot change the result of the logic expression, so they do not need to be executed, thus we save time.

See example below.


Process

Remember that a parent process creates offspring processes which in turn create other processes and so on. This leads to a hierarchy of processes (or a tree one could say).

Having this in mind, it's worth taking a look at this similar problem, as well as this answer.


Descriptive image

I made also this figure which can help, I guess. I assumed that the pid's fork() returned are 3, 4 and 5 for every call.

fork nodes Notice that some fork()s have a red X above them, which means that they are not executed because of the short-circuiting evaluation of the logic expression.

The fork()s at the top are not going to be executed, because the first operand of the operator && is 0, thus the whole expression will result in 0, so no essence in executing the rest of the operand(s) of &&.

The fork() at the bottom will not be executed, since it's the second operand of a ||, where its first operand is a non-zero number, thus the result of the expression is already evaluated to true, no matter what the second operand is.

And in the next picture you can see the hierarchy of the processes: Process hierarchy based on the previous figure.


Example of Short Circuiting

#include <stdio.h>  int main(void) {    if(printf("A printf() results in logic true\n"))     ;//empty body    if(0 && printf("Short circuiting will not let me execute\n"))     ;   else if(0 || printf("I have to be executed\n"))     ;   else if(1 || printf("No need for me to get executed\n"))     ;   else   printf("The answer wasn't nonsense after all!\n");    return 0; } 

Output:

A printf() results in logic true I have to be executed 
like image 173
gsamaras Avatar answered Oct 19 '22 09:10

gsamaras


The first fork() returns a non-zero value in the calling process (call it p0) and 0 in the child (call it p1).

In p1 the shortcircuit for && is taken and the process calls printf and terminates. In p0 the process must evaluate the remainder of the expression. Then it calls fork() again, thus creating a new child process (p2).

In p0 fork() returns a non-zero value, and the shortcircuit for || is taken, so the process calls printf and terminates.

In p2, fork() returns 0 so the remainder of the || must be evaluated, which is the last fork(); that leads to the creation of a child for p2 (call it p3).

P2 then executes printf and terminates.

P3 then executes printf and terminates.

4 printfs are then executed.

like image 44
Jean-Baptiste Yunès Avatar answered Oct 19 '22 08:10

Jean-Baptiste Yunès