uyetch
uyetch

Reputation: 2190

Confusion in the output of fork()

I have been learning about the fork() function for the past few days and have been doing some experiments to know how it actually works. While doing so, I cam across this interesting piece of code which I failed to understand. Here is the code:

int main(int argc, char *argv[])

{
int p,m;
    p = getppid();
    printf("%d\n",p);

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
}

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
}

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
}

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
}



return 0;
}

The output to this is:

$./a.out
6117
6460
1
user@ubuntu:~/forkbomb$ 1
1
1
1
1
1
1
1
1
1
1
1
6473

It would be really nice of you, if you explain to me why is the pid of init which is 1 appearing the output. If it helps I would like to clarify that I was trying to create 5 processes from a given one. So can you please tell me the correct way to do so too ? Thanks

Upvotes: 0

Views: 339

Answers (5)

Adam Zalcman
Adam Zalcman

Reputation: 27233

If child process's parent exits or dies before the child, then the child is called an orphaned process and is adopted by init. This means that PPID (parent PID) of the child changes to 1. This explains your output since it comes from getppid().

To explain the number of lines that was displayed, let's number the lines of code:

 1  int p,m;
 2      p = getppid();
 3      printf("%d\n",p);
 4  
 5  if(fork() == 0) {
 6      p = getppid();
 7      printf("%d\n",p);
 8  }
 9  
10  if(fork() == 0) {
11      p = getppid();
12      printf("%d\n",p);
13  }
14  
15  if(fork() == 0) {
16      p = getppid();
17      printf("%d\n",p);
18  }
19  
20  if(fork() == 0) {
21      p = getppid();
22      printf("%d\n",p);
23  }

Let's now count all the processes that executed each printf(). The printf() on line 3 was obviously executed only by the original parent process. The printf() on line 7 was only executed by the first child of the original parent process since fork() returns 0 in the child. Now, line 9 is reached by two processes: the original parent and its first child. Both of them fork and both of their children (i.e. second child of the original parent and the first child of the first child of the original parent) execute the printf() on line 12. Line 14 is reached by 4 processes (the original parent, its both children and the first child of the first child of the original parent). All of them spawn a child on line 15 and all the four children execute the printf() on line 17. As many as 8 processes reach line 19. Each of them forks and 8 resulting children in the youngest generation execute the final printf() on line 22.

First printf() is executed 1 time. Second printf() is executed 1 time. Third printf() is executed 2 times. Fourth printf() is executed 4 times. Fifth printf() is executed 8 times.

This is 16 in total and is consistent with your output. Some of the PPIDs displayed are equal 1 indicating that a given parent executed so fast that the child was adopted by init before it reached a given printf(). Others are greater than 1 indicating that the parent of a given process was still running when the printf() was reached in the child. It is extremely likely, that multiple executions of the program will result in somewhat different output.

Thus, you do not create 4 child processes, but 15. This is due to the fact that your children continue executing from when the fork() that spawned them returns. This means that some of the fork()s will be executed not only by the original parent, but also by its children, creating a cascade of new processes. If you want to create only 4 children, you should make sure that remaining forking happens in the parent only.

Upvotes: 2

lastland
lastland

Reputation: 910

This program would create 1 + 2 + 4 + 8 = 15 processes in the four fork() parts(16 if we plus the original one). Maybe what you want to do is something like this:

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
} else
    return 0;

getppid() would return 1 if its parent had been gone. Since we don't know how the system would schedule these processes, you can see this could happen in your program.

Upvotes: 0

Has QUIT--Anony-Mousse
Has QUIT--Anony-Mousse

Reputation: 77454

First of all, the 1's come from the parent process(es) having exited already. The parent then becomes the system init process, 1.

If you are also irritated by the number of 1s you get: The initial process forks 4 children, the first child of this forks 3, there are two children that fork 2 each, three children that fork 1. and four that don't fork any further. That makes 1+1+4+3+2+1+4 = 16 processes total.

Upvotes: 0

stefanB
stefanB

Reputation: 79770

What you missed is that when you fork there are 2 processes with the same copy of the code continuing from the point where you forked.

The parent gets pid of child returned and the child gets 0 returned. So you are forking 16 processes (4 factorial) because at each fork you are doubling the number of processes.

If you add sleep() at the end to make sure that the parent processes hang around long enough then you get the actual parent pid in child (just add sleep(2) before return).

My output is:

> ./x
19291
21686
21686
21687
21688
21687
21687
21688
21689
21691
21690
21689
21695
21686
21686
21694

Upvotes: 0

Mat
Mat

Reputation: 206659

The parent starts by printing its parent's PID. Then it proceeds to fork four children (C1..4), and exits.

C1 prints its parent PID, then proceeds to fork three children of its own. C2 prints its parent PID, then proceeds to fork two children of its own. C3 prints its parent PID ...

Each forked child continues running after the if block that created it, so quite a few children will be created.

When the parent process exits, the children are reparented to init, which has process id 1. The exact output will vary from run to run, depending on exactly when/how the children are scheduled and the parents exit.

If you want to create only five processes, make sure the children exit when they are done!

if(fork() == 0) {
    p = getppid();
    printf("%d\n",p);
    exit(0);
}

If you want to parent to wait for its children to all have completed before it exits itself, you should look into the wait family of functions.

Upvotes: 3

Related Questions