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;
}
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.
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.
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.
Related
This is a question from one of my tasks and I'm having some confusion with it.
int main()
{
printf("line\n");
pid_t pid = fork();
fork();
fork();
if(pid == 0)
fork();
fork();
printf("line\n");
return 0;
}
How many processes would be created from the execution of the code?
From executing the code, it would generate 28 lines of output and just not sure how to find the amount of processes created in this statement.
There will be a total of 24 processes. Here is how.
After the first fork() there are two processes. In one of them pid == 0; in the other process pid != 0.
After second fork, there are 4 processes (half of them have pid == 0).
After third fork, there are 8 processes (and half of them have pid == 0).
The fourth fork statement is executed only by those processes where pid == 0. So 4 processes will execute fork (and turn into 8 processes). The other 4 processes will not execute fork and will remain 4. Alltogether we have 8+4=12 processes.
Finally, another fork turns our 12 processes into 24.
How many threads would be created from the execution of the code?
Now is the question of semantics. Do you say that processes are not threads, and so 0 threads are created? Do you say that out of 24 processes, the first one is the original process and not created by this code, so a total of 23 processes are created? This is a question of semantics, not of software, so we can't help you with this.
Number of lines printed
The first printf is executed by the original process only (since it has not forked yet). The second printf is executed by all 24 processes (just before each process terminates). So a total of 25 lines is printed.
As above, 24 leaves produce.So 24 processes produce finally.And before your first fork, "line" will be printed one time. After you fork, because of 24 processes, 24 "line" will be printed. So you will have 25 "line".
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;
}
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.
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:
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
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.
For all the downvoters, this is from a merged but different question. Blame SO. Thank you.
You can decompose the problem to three lines, the first and last lines both simply double the number of processes.
fork() && fork() || fork();
The operators are short-circuiting, so this is what you get:
fork()
/ \
0/ \>0
|| fork() && fork()
/\ / \
/ \ 0/ \>0
* * || fork() *
/ \
* *
So this is altogether 4 * 5 = 20 processes each printing one line.
Note: If for some reason fork() fails (for example, you have some limit on the number of processes), it returns -1 and then you can get different results.
Executing fork() && (fork() || fork()), what happens
Each fork gives 2 processes with respectively values pid (parent) and 0 (child)
First fork :
parent return value is pid not null => executes the && (fork() || fork())
second fork parent value is pid not null stops executing the || part => print forked
second fork child value = 0 => executes the || fork()
third fork parent prints forked
third fork child prints forked
child return value is 0 stop executing the && part => prints forked
Total : 4 forked
I like all the answers that have already been submitted. Perhaps if you added a few more variables to your printf statement, it would be easier for you to see what is happening.
#include<stdio.h>
#include<unistd.h>
int main(){
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld\n", getpid(), child);
return 0;
}
On my machine it produced this output:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
This code:
fork();
fork() && fork() || fork();
fork();
gets 20 processes for itself and 20 times Printf will go.
And for
fork() && fork() || fork();
printf will go a total of 5 times.
#include<iostream>
#include<unistd.h>
#include<stdio.h>
using namespace std;
int main()
{
fork();
fork();
fork();
fork();
printf("*"); /*This prints 16 stars*/
return 0;
}
When working with fork(), why does it print 16 *'s?
I understand that fork() generates a new
child process that both execute the same process which would explain why one fork generates 2 stars but, with four forks it prints 16 which I can see that it doubles with each fork().
But I am not understanding why. Is each fork executing the functions and parameters below it?
Because the first fork will split into two process, the second fork() call will be called by those two processes, splitting those two processes into 4. This will continue until all the fork() calls have been called in each process. So you end up having 2^4 = 16 calls to printf("*")
In the "diagram" each bar represents the number of processes that are executing when the function is called. So the function is executed as many times as there are bars.
| fork() // The first processes creates a second (2 total)
| fork() | // Those 2 processes start 2 more (4 total)
|| fork() || // Those 4 processes start 4 more (8 total)
|||| fork() |||| // Those 8 processes start 8 more (16 total)
|||||||| printf() |||||||| // resulting in 16 calls to printf()
Is each fork executing the functions and parameters below it?
Yes, as you can see from the diagram, when a process forks the created process (and the one that created it) continues execution on the next instruction after the fork.
When you call fork(), it create a new process. You duplicate your application, and then the 2 applications continues to run and execute new instructions after the fork() call
printf("i'm the main thread\n");
fork();
printf("i'm executed 2 times\n");
fork(); //this fork is so executed 2 times, so 2 new processes, so 4 processes for all
printf("i'm excecuted 4 times\n");
fork(); //this fork is executed 4 times to ! So you have now 8 processes;
// and etc ..
fork(); //this fork is executed 8 times, 16 process now !
printf("*"); // executed 16 times
The new processes share all memory before the fork(), old changed states are for the current thread.
if you want to do anothers things depending the process :
pid_t p;
int i = 1;
p = fork();
if(p == 0)
{
printf("I'm the child\n");
i = 2;
exit(0); //end the child process
}
printf("i'm the main process\n");
printf("%d\n", i); //print 1
How we can get this process with this condition??schema of process?
int main (int argc, char **argv) {
int i;
int pid;
for (i= 0; i < 3; i++) {
pid = fork();
if (pid < 0) break;// with this condition i dont understand??
}
while (wait(NULL) != -1);
fork() splits a process in two, and returns 0 (if this process is the child), or the PID of the child (if this process is the parent), or -1 if the fork failed. So, this line:
if (pid < 0) break;
Says "exit the loop if we failed to create a child process".
The diagram is a little confusing because of the way the processes (circles) correspond to the fork() calls in the loop. The three child processes of the main process are created when i is 0, 1, and 2 respectively (see the diagram at the bottom of this post).
Since the loop continues in both the parent and the child process from the point fork was called, this is how the forks happen:
i == 0: fork is called in the original parent. There are now two processes (the top one, and the left one).
i == 1: fork is called in the two existing processes. New children are the leftmost child on the second layer from the bottom, and the middle child on the third layer from the bottom. There are now four processes
i == 2: fork is called in all existing processes. New children are all remaining nodes (the bottom node, the two rightmost nodes in the second layer from the borrom, and the rightmost node in the third layer from the bottom)
i == 3: All 8 processes exit the loop
Here is the diagram again, with numbers indicating what the value of i was in the loop when the process was created:
-1 <--- this is the parent that starts the loop
/ | \
0 1 2
/ \ |
1 2 2
|
2
To understand your diagram you must rely on the behavior of fork: it splits the process in two, creating another process identical to the first (except for the PID) in a new memory location.
If you call it in a loop that's what happen:
When i=0 the first process will be split, creating another process that will start running from exactly this point on (so will skip the first loop). Focusing on the first process, it will continue the loop, generating another process when i=1. The second process, thus, will start from i=1, so will skip the first two loops. The first process will be split last time for i=2. The last copy created, however, will start running from i=2, so it will exit the loop and will not generate anything.
The first copy created will start the loop from i=1, generating two process, while the second copy will start from i=2, generating only one copy.
You can continue this reasoning and understand the rest of the diagram.
As others pointed out, if (pid < 0) is just a check to see if there are errors and does not modify the logic of the code.
fork returns -1 if the fork call failed. it returns the pid in parent and 0 in the child. The condition you're looking at doesn't really matter to the functioning of the code; it's just saying if there's an error with fork then exit the loop. If there's no error in the fork call then the process tree in your diagram will be built.
The reason why is that the same loop will continue running in the child processes. So the children will also continue to fork based on the value of i at the time fork was called.
fork return -1 on error, and 0 or positive else, so the line if (pid < 0) break; says "if there was error, exit from the loop".
Assuming that there is not error, it's something like:
At the beginning, i=0, and you have one process. let's call it p0.
In the line fork();, p0 creates another process. let's call it p1.
In everyone of them, we have i++ (so now i is 1), and we are iterating the loop again.
p0 and p1, separately, have a fork(); command, so everyone of them creates another process. let's call the new processes p2 and p3.
Now, in every process, we have i++, that set i to be 2, and we run the loop again.
Everyone of the 4 processes we have, run the line fork();, and creates a new process. so now we have also p4,p5,p6,p7.
Every process increase its i to 3, and then, since the loop condition is now false, the loop finally ends.
Now, the 8 process arrive (separately) to the next line.
(In fact, every iteration double the number of processes, so if you change the 3 to, for example, 15, you will have 2^15 processes at the end.)
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;
}
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.
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:
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
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.
For all the downvoters, this is from a merged but different question. Blame SO. Thank you.
You can decompose the problem to three lines, the first and last lines both simply double the number of processes.
fork() && fork() || fork();
The operators are short-circuiting, so this is what you get:
fork()
/ \
0/ \>0
|| fork() && fork()
/\ / \
/ \ 0/ \>0
* * || fork() *
/ \
* *
So this is altogether 4 * 5 = 20 processes each printing one line.
Note: If for some reason fork() fails (for example, you have some limit on the number of processes), it returns -1 and then you can get different results.
Executing fork() && (fork() || fork()), what happens
Each fork gives 2 processes with respectively values pid (parent) and 0 (child)
First fork :
parent return value is pid not null => executes the && (fork() || fork())
second fork parent value is pid not null stops executing the || part => print forked
second fork child value = 0 => executes the || fork()
third fork parent prints forked
third fork child prints forked
child return value is 0 stop executing the && part => prints forked
Total : 4 forked
I like all the answers that have already been submitted. Perhaps if you added a few more variables to your printf statement, it would be easier for you to see what is happening.
#include<stdio.h>
#include<unistd.h>
int main(){
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld\n", getpid(), child);
return 0;
}
On my machine it produced this output:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
This code:
fork();
fork() && fork() || fork();
fork();
gets 20 processes for itself and 20 times Printf will go.
And for
fork() && fork() || fork();
printf will go a total of 5 times.