Reputation: 2418
The child process begins executing at the exact point where the last one left off - after the fork statement. What if the statement contains multiple fork()s like the conditional expression like the following. Where exactly program execution starts for the child process. Before worrying about how many processes gets created, I wanted to know if every child process created tries to evaluate fork() && fork() || fork();
statement. If it is so. How does a child process that got created because of second fork() statement have information from the first fork() to evaluate fork() && fork()
.
main(){
fork() && fork() || fork();
}
Upvotes: 2
Views: 1561
Reputation: 224884
The child that results from the second fork()
knows about the results of the first fork()
because it is an exact copy of the parent process.
You can work out what happens by drawing a little tree for yourself. Start with the first fork:
fork()
/\
/ \
parent --/ \-- child1
The parent gets back the PID of the child1
process, and child1
gets back 0. So we have something like:
PID(child1) && fork() || fork()
in the parent, and:
0 && fork() || fork()
in the child. Short circuiting means that the middle fork()
of the original expression doesn't get executed in the child, only in the parent. So now what happens to the tree?
fork()
/\
/ \
parent --/ \-- child1
fork()
/\
/ \
parent --/ \-- child2
parent
is the original process, and gets the PID of child2
. child2
, just like child1
, gets 0. What do our expressions look like now?
parent: PID(child1) && PID(child2) || fork() = 1 || fork()
child: 0 || fork()
child2: PID(child1) && 0 || fork() = 0 || fork()
Now, again by short-circuiting, parent
is done, and doesn't execute the last fork()
. Both child
and child2
have to, however. That leaves us with the following tree:
fork()
/\
/ \
parent --/ \-- child1
fork() fork()
/\ /\
/ \ / \
/ \ child1 --/ \-- child1-1
/ \
/ \
parent --/ \-- child2
fork()
/\
/ \
child2 --/ \-- child2-1
And that's it. child1
and child2
each get the PID of their respective children, and child1-1
and child2-1
each get back 0. Substituting those values in, the final expressions are:
parent: 1
child1: 0 || PID(child1-1) = 1
child2: 0 || PID(child2-1) = 1
child1-1: 0 || 0 = 0
child2-1: 0 || 0 = 0
And that's it - they all exit.
Upvotes: 7
Reputation:
fork() && fork()
Is equivalent to writing:
pid_t x = fork();
if ( x > 0 )
{
fork();
}
That is, the parent process will fork twice, since both conditions are evaluated; neither child will fork()
since both return 0 from the fork. However, fork() || fork ()
is equivalent to:
pid_t x = fork();
if ( x == 0 )
{
fork();
}
So, the parent process will fork()
once and its child will fork()
once.
If you speak asm, try spitting out the output from these two expressions:
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
call fork
testl %eax, %eax
je .L3 // jump if eax is zero
call fork
testl %eax, %eax
nop
.L3:
call fork
testl %eax, %eax
jne .L7 // jump if eax is non-zero.
call fork
testl %eax, %eax
Note that call fork
is the point at which your process loses control and then regains it with eax
set to the return value. This question will help understand testl
.
You should be able to work out what would happen in your combined example using the operator precedence rules.
Upvotes: 1
Reputation: 182763
I'm not sure what could be confusing. When the fork
function is called, it creates a second process that is a duplicate of the existing process. Both processes continue from precisely where they left off. The child knows how to continue evaluating the same way the parent does because it has precisely the information it needs in precisely the place it needs it because it is a clone.
Upvotes: 1