Fork() and Output

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:

  1. Fater 3124 invokes the printf
  2. Child 3125 invokes the printf

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:

  1. Fater 3124 invokes the printf
  2. Child 3126 invokes the printf

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.

Sample Image

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 be 4 times a single process produces

    fork()&&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 && goes wrong, we don't check the second. Similarly, if first of the two operands of || is true, 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

  1. 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 set errno to a value indicating why the fork(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.
  2. the wait(NULL); is not in the if (pid == 0), but in the else part, so it is indeed in the if (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 a wait(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 you exec(2) some other program in the child's process) You can learn if your child died because it exit(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:

  1. child push character 'a' into buffer, then 'c' into buffer.
  2. parent simultaneously pushes character 'b' into buffer and waits for the child.
  3. child exits and flushes buffer containing "ac" before that.
  4. parent returns from waitpid() and pushes 'c' into buffer.
  5. 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



Leave a reply



Submit