The_Student
The_Student

Reputation: 47

C program yields two process trees instead of one

If my interpretation is right, from the following C program one expects to obtain one process tree, where from one father process, three child processes and two grandchild processes are generated:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int num;
    pid_t t;
    for (num = 0; num < 2; num++) {
        t = fork();
        if (t == 0)
        break;
    }
    t = fork();
    printf("I am the process %d and my father is %d\n", getpid(), getppid());
    return 0;
}

However, after compiling and executing, the output turns out to be as follows:

I am the process 2133 and my father is 2127
I am the process 2134 and my father is 2133
I am the process 2137 and my father is 778
I am the process 2135 and my father is 778
I am the process 2138 and my father is 778
I am the process 2136 and my father is 778

As expected, six processes are produced, but four of them are spawned by the process #778. On the other hand, just one of them (#2134) seems to be fathered by the initial process (#2133). In total two process trees have been produced instead of one.

Why this behavior? Does it mean that those above mentioned four processes have been adopted/reclaimed by the process #778?

Upvotes: 2

Views: 347

Answers (2)

Luis Colorado
Luis Colorado

Reputation: 12698

I have executed your code, by printing at first the pid of the root process, to obtain this output:

$ pru
start: pid == 6024
I am the process 6025 and my father is 6024
I am the process 6024 and my father is 5524
I am the process 6027 and my father is 6025
I am the process 6028 and my father is 6024
I am the process 6026 and my father is 6024
I am the process 6029 and my father is 6026

I can reorder the data to build the tree, starting by process 6024:

I am the process 6024 and my father is 5524
  I am the process 6026 and my father is 6024
    I am the process 6029 and my father is 6026
  I am the process 6025 and my father is 6024
    I am the process 6027 and my father is 6025
  I am the process 6028 and my father is 6024

you could have done the same, by looking that process 6024 shows a completely different parent (in this case 5524) that is not in the list of processes.

In your case, the output is bad (or you have retouched it or the posted code is not the same that produced that output) as the process tree is not the one shown here.

  • process 6024 executes the full loop, and so, executes three fork()s, creating tree more processes (two in the loop and one after it).
  • two of these processes (the ones created in the loop: 6025 and 6026) break the loop, and execute the fork() outside the loop, creating one process each, by calling the fork() that is outside the loop. giving you processes 6027 and 6029, resp.

The output you show, cannot be arranged in this tree structure, if we sort the lines:

I am the process 2133 and my father is 2127
  I am the process 2134 and my father is 2133
I am the process 2137 and my father is 778
I am the process 2135 and my father is 778
I am the process 2138 and my father is 778
I am the process 2136 and my father is 778

as only process 2134 is a child of other process... and that is not a tree.

Note

I have read in the comments to your question that for some reason the children have been reparented to the shell. In UNIX no process is reparented to another parent, except when it's parent exit()s *by any means), in which case, they are reparented to process with PID=1 (the init or systemd process, and this is what makes pid 1 special) So your process can be reparented, but never to a different process than the one with PID=1. Indeed, you can check if your parent has died, by testing the result of getppid() as, if it returns 1, then your parent is not alive anymore.

Finally, there's plenty of documentation about this schema and the unix process structure, but if I had to recommend you to read about it, I should refer you to the POSIX specifications, available from The Open Group

Upvotes: 1

ggorlen
ggorlen

Reputation: 57185

As pointed out in the comments, you're not waiting for the children to finish in the parent, so orphaned processes are being reclaimed by the shell after their parents exit.

The correct tree structure is:

   1
   |
   2
 / | \
3  4  5
|  |
6  7

where:

  • 1 is the shell
  • 2 is your main program; it spawns 3 and 4 in its loop and 5 after the loop
  • 3 and 4 are children spawned in the loop by 2; each spawns 1 new child (6 and 7) after it exists the loop on break
  • 5 is spawned after the loop and therefore is a leaf process
  • 6 and 7 are leaf processes spawned after the loop by 3 and 4

Here's cleaned-up code that will give comprehensible output:

#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>

int main() {
    printf("original process is %d\n", getpid());

    for (int num = 0; num < 2; num++) {
        if (fork() == 0) { // 0 means child
            break;
        }
    }

    fork(); // fork after the loop
    printf("I am %d and my parent is %d\n", getpid(), getppid());

    while (wait(NULL) > 0) {} // wait for each child process

    return 0;
}

Sample output:

original process is 540
I am 540 and my parent is 1
I am 541 and my parent is 540
I am 543 and my parent is 541
I am 542 and my parent is 540
I am 544 and my parent is 540
I am 545 and my parent is 542

Upvotes: 2

Related Questions