Why is the output of a fork() in a particular order?
You can't really determine which process will be executed first. Like HackerBoss stated the printf
can also influence this order.
Imagine your main program pid (3124
) generates child 3125
. After generating the child the following instruction needs to be invoked by both father and child:
printf("2.fork() from ");
At this point there are two directions:
- Fater
3124
invokes theprintf
- Child
3125
invokes theprintf
Since the printf
requires I/O scheduling
it depends on the process priority
and on the resource state
(there could be another process already already using the resource, making it a busy resource
).
So it looks like in your program the father 3124
gets access to the resource first and continues executing up to the next fork, where child 3126
gets generated.
At this point there is the same question: in which direction shall I go? The next instruction is:
printf("3.fork() from ");
Directions are:
- Fater
3124
invokes theprintf
- Child
3126
invokes theprintf
From your program it looks like the first process to invoke it is child 3126
.
So actually the printf
does not assure you the process generation order. Since it is transparent how the I/O scheduling
works, a better way would be to store a value in a specific address different for each process, by wrapping the fork
in a if
statement:
pid=fork();
if (pid == 0) {
//child process
} else {
//father process
}
This way you can have a better idea on what the process scheduler
is doing, since it could actually be the process scheduler
that starts a child before another, there are a lot of scheduling algorithms. At this point the OS
you are running also influences the process execution order, depending on the used algorithm.
Output of fork() calls
Saikacollection will be printed 40
times as output to the code. This can be explained as follows :-
To understand the output, we need to know following :-
- On successful execution of a fork() call, new child is created. The process creating the child is called
parent process.
- Fork() call returns
pid
(process identifier) of the child to parent - Fork() returns
0
to the child process.
Consider the image shown :-
Convention : All the parents(callers) of the process are written to left
and marked with a star
.
fork()
At the beginning, we have just 1 process, so a fork() call creates a child. Considering the root of the tree as level 1, we can see at level 2 , we have two processes, parent(left) and child(right) .
fork()
fork() again further creates
4
such processes, marked as 1, 2, 3, 4. Since all of the four processes will go through similar code structure further, we can say the total number of processes will be4
times a single process producesfork()&&fork()||fork()
Understanding this statement involves, realizing the fact that in C,
&& operator has more precedence than ||
- Also, if
first
of two operands joined by&&
goeswrong
, wedon't check the second
. Similarly, iffirst
of the two operands of||
istrue
,we don't check the second
operand. - Consider, the fork() call at 1(marked) node, two processes are created. While the parent gets a positive number(pid) as return, child gets a 0. So, parent executes the second operator, while the child jumps to fork() after || as marked in the figure.
- The execution of
&&fork()
for the parent at 4th level, returns a pid for one process, which terminates, while the child of that step, gets a 0. So, it goes for execution of||fork()
- the final
||fork()
call for child of level 5, further produces a process as a result - So, at the end of the step, we have 5 leaves(processes) as marked by underlines in the figure.
Had we done the same for all the three nodes, we could have got
5*4 = 20
processes.fork()
Final fork() just
doubles
the number of process available at that step.- So, total number of processes =
2*20 = 40
.
How to know the output in the code with fork()?
ACCBB
is not possible.
I'll add a subscript p for letters printed by the parent, c for the child.
The parent process prints ApBpCp in that order, and the child process prints BcCc in that order.
Ap will be printed first, because it's printed before anything is forked. The parent waits for the child to exit before printing Cp, so this has to come last. So this means the first and last characters are A
and C
.
In between this, there are no constraints on the order of execution between the two processes. So the following are possible:
BpBcCc
BcBpCc
BcCcBp
The first two look the same in output, since we can't tell the difference between Bp and Bc
So the possible results are:
ABBCC
ABCBC
This all assumes that there's no buffering that might delay the output. I.e. this is just an exercise to examine the order that statements can be executed in concurrent processes, not really about I/O.
fork() and output
This isn't quite what you thought originally. The output buffer is not shared - when you execute the fork, both processes get a copy of the same buffer. So, after you fork, both processes eventually flush the buffer and print the contents to screen separately.
This only happens because cout is buffered IO. If you used cerr, which is not buffered, you should only see the message one time, pre-fork.
Trouble understanding fork() output
You know that
The return value is the zero in the child and the process-id number of the child in the parent, or -1 upon error.
So, let's see step by step what's happening here.
When fork()
is called, it creates a new child with id n
, then returns in the child 0
and in the parent n
.
So let's suppose our process as pid 1
, when the first fork()
is called it creates a process with pid 2
, then returns to a a value. a
will have value 0
in the process 2
(the child), and will have value 2
in process 1
(the parent).
Then each process will call fork()
and assign the return value to b
in the parent process. In the child, b
will have value 0
.
Anyway, I think this schema will simplify the comprehension:
The main starts:
|
|
int a = fork(); // It creates a new process, and the old one continues going
|
|-------------------------|
a = 2; /* Parent */ a = 0; // Child
| |
| |
int b = fork(); int b = fork(); // Each one create a new process
| |
| |-----------------------------|
| /* Child -> Parent */ // Child -> Child
| a = 0; b = 4; a = 0; b = 0
|
|
|
|
|-----------------------------|
/* Parent -> Parent */ // Parent -> Child
a = 2; b = 3; a = 2, b = 0;
Output from fork() in c
The
fork(2)
system call creates a second, child process and as a consequence, the call is called once in the parent, but returns in both, the parent and the child.Due to this, the code following the call needs some indication to know if we are executing code in the parent or the child, because this allows parent and child to divert from the common code from this point on.
The definition of
fork(2)
and it is stated in the manual page is:- to return
-1
and seterrno
to a value indicating why thefork(2)
call failed. - to return
0
for the child subprocess. - to return the
pid_t
child process id to the parent process (a positive number) so it can know the pid of the new subprocess just started.
- to return
the
wait(NULL);
is not in theif (pid == 0)
, but in theelse
part, so it is indeed in theif (pid != 0)
or in the parent process. The parent process must know if the child has finished and how, and must synchronise with it, for several reasons:the child can
exit(2)
before the parent, but depending on the child's amount of work, it can do after the parent finishes. In case the parent ends before the child, you'll get the shell prompt again, and your screen will be blurred with the output of the child, right after the prompt has been issued. This is not what you normally desire, so await(2)
call in the parent is just polite, so the shell only prompts you after everything has finished.A good test is to comment the
wait(2)
call in your example and see what happens.the
wait(2)
system call is a means of knowing how your child ended it's run (mostly if youexec(2)
some other program in the child's process) You can learn if your child died because itexit(2)
ed (and receive its exit code), because it was interrupted (and what the signal it received was) or if it has been stopped by the user/system for some reason.
A reading of fork(2)
, exec(2)
, exit(2)
and wait(2)
system calls (all as a group of related calls) will enlighten all the process relationships and how all these system calls collaborate to manage the unix process system.
fork() Parent and child Processes of output
Theoretically, your answer is correct, it could happen like this (so at the end (a), (c), (d) seem they could happen).
Practically, the only correct answer is (a).
The reason for that is that stdio
uses internally buffers for caching the output and avoiding expensive system calls. Therefore, until your program outputs `\n' (newline) or exits, there is no output at all.
So the real scenario is going to be:
- child push character
'a'
into buffer, then'c'
into buffer. - parent simultaneously pushes character
'b'
into buffer and waits for the child. - child exits and flushes buffer containing
"ac"
before that. - parent returns from
waitpid()
and pushes'c'
into buffer. - parent exits and flushes buffer containing
"bc"
.
About the second part:
SIGKILL
can kill any process (apart from some system processes). Child process is regular process like any other.
waitpid
is to wait for child process until it exits. It has nothing to do with killing processes, it only waits (either due its own exit or due to being killed, no matter by which signal).
Related Topics
When Is a Function Try Block Useful
Class Template Argument Deduction Not Working with Alias Template
Why Can't "Transform(S.Begin(),S.End(),S.Begin(),Tolower)" Be Complied Successfully
Why Does Clang Optimize Away X * 1.0 But Not X + 0.0
When Is It Worthwhile to Use Bit Fields
On Local and Global Static Variables in C++
Writing a Simple Equation Parser
How to Check String Start in C++
Checking If an Iterator Is Valid
Why Destructor Is Not Called on Exception
What Is the Underlying Type of a C++ Enum
How to Store a 64 Bit Integer in Two 32 Bit Integers and Convert Back Again
Automatically Adding Enter/Exit Function Logs to a Project
C++ Class Member Function Pointer to Function Pointer
How to Reduce the Size of Executable Produced by Mingw G++ Compiler