Home > Back-end >  Behavior of a C program using fork()
Behavior of a C program using fork()

Time:12-07

Given the following code, I have to check its behavior, that is, how much processes are produced.

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char*argv[])
{
    int pid, i , j;
    pid_t t;
    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

    printf("I am the process %d and my father is the process %d\n", getpid(), getppid());

    while(wait(NULL)>0) {}

    return 0;
}

After compiling and executing, the output is as follows:

I am the process 3749 and my father is the process 2254

I am the process 3750 and my father is the process 3749

I am the process 3751 and my father is the process 3749

I am the process 3752 and my father is the process 3749

I am the process 3754 and my father is the process 3749

I am the process 3753 and my father is the process 3750

I am the process 3755 and my father is the process 3750

I am the process 3756 and my father is the process 3752

I am the process 3757 and my father is the process 3752

I am the process 3758 and my father is the process 3756

I am the process 3759 and my father is the process 3756

This means the corresponding tree process looks like this:

                      2254
                       |
                   -- 3749 ----
                  /   |   \    \
                3750 3751 3752 3754
               /   \      /  \
            3753  3755  3756 3757
                       /   \
                     3758  3759

In total, 11 processes are produced, if we count the initial process.

I expected much more processes to be generated than just 11; anyway, I am able to understand the spawning of all processes but the most distant leaves (3758 and 3759).

In the program there are two for loops, being one nested into the other. As two break statements are inside each loop, I guess that a loop or the two should finish at some point before their natural end.

Could somebody confirm this code's behavior?

Thank you in advance

CodePudding user response:

These kinds of programs are more to illustrate that you understand how forking works, I hope you never write a forking program within multiple nested loops complete with breaks.

They way you track this is by simulating the machine on paper, but each time you simulate the fork, you copy the simulation, setting the return code of fork() for one copy to zero, and the return code of the other copy to the other process's pid.

Let me reformat your program to make it easier

    int pid;
    int i;
    int j;
    pid_t t;

will hold our "current values" for a process. We will also indicate the instruction pointer location with "IP ==>"

    int pid;
    int i;
    int j;
    pid_t t;
    IP ===>
    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

Next step (for PID 1)

    === PROCESS 1 ====
    int pid;
    int i; (set to 0)
    int j;
    pid_t t;

    for (i = 0; i < 2; i  ) {
    IP ===>
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PID 1)

    === PROCESS 1 ====
    int pid (set to 1  // fake pid, but easy to count)
    int i; (set to 0)
    int j;
    pid_t t;

    for (i = 0; i < 2; i  ) {
        pid = getpid();
    IP ===>
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for process 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t;

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
    IP ===>
            t = fork();
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PID 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
   IP ===>
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PID 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 2, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  
   IP ===>
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  
                t = fork();
   IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  
                t = fork();
   IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  
                t = fork();
                break;
            }
        }
    IP ===>
        if (pid != getpid())
            break;
    }
    === PROCESS 2 ===
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int pid (set to 1)
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 0)
    int j; (set to 1, due to j  )
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;
    IP ===>
    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1, due to j  )
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2; IP ===> i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1, due to j  )
    pid_t t; (set to 3, parent process)

    for (i = 0; IP ===> i < 2;  i  ) { // 1 is less than 2 so enter loop block
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1)
    pid_t t; (set to 3, parent process)

    for (i = 0; i < 2;  i  ) {
    IP ===>
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1, again)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 1)
    pid_t t; (set to 3)

    for (i = 0; i < 2;  i  ) {
        pid = getpid();
    IP ===>
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

next step (for PROCESS 1)

    === PROCESS 1 ====
    int pid (set to 1)
    int pid = 1;
    int i; (set to 1)
    int j; (set to 0)
    pid_t t; (set to 3)

    for (i = 0; i < 2;  i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
    IP ===>
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
                break;
            }
        }
        if (pid != getpid())  // pid == 1, getpid == 1. so skip break
            break;

    }
    === PROCESS 2 ===
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child process)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
    IP ===>
            if (t != 0) {
                t = fork();
                break;
            }
        }
        if (pid != getpid())
            break;
    }
    === PROCESS 3 ====
    int i; (set to 0)
    int j; (set to 0)
    pid_t t; (set to 0, child of PID 1)

    for (i = 0; i < 2; i  ) {
        pid = getpid();
        for (j = 0 ; j < i 2; j  ){
            t = fork();
            if (t != 0) {  // t == 2 in this process, so enter the block
                t = fork();
    IP ===>
                break;
            }
        }
        if (pid != getpid())
            break;
    }

And so on. Once you get Process 1 out of it's loops (and waiting for its children complete), you follow the same procedure for Process 2 until it is out of it's loops and waiting for it's children to complete.

The point of this exercise is if you can simulate some confusing process code. It is clear that since the top level process will loop twice, and it never breaks out of the loop, you will get 4 processes under it. As you practice stepping through the other processes, you will start to see the patterns they implement.

Personally, this kind of problem is a lot of work, and not the kind of work that makes you a better programmer; but, it will demonstrate if you understand that a program is forked with it's current state (notice that pid is wrong in many of the children, because it wasn't updated after the fork), and if you understand that fully, you can simulate the program to it's final completion.

Of course, we use computers to automate boring stuff (like this), so you might want to come up with a new logging line like this:

 void printState(int me, int pid, int i, int j, int t) {
     printf("PID %d: pid = %d, i = %d, j = %d, t = %d\n");
 }

and then run

   printState(getpid(), pid, i, j, t);
   for (i = 0; i < 2; i  ) {
        printState(getpid(), pid, i, j, t);
        pid = getpid();
        printState(getpid(), pid, i, j, t);
        for (j = 0 ; j < i 2; j  ){
            printState(getpid(), pid, i, j, t);
            t = fork();
            printState(getpid(), pid, i, j, t);
            if (t != 0) {
                printState(getpid(), pid, i, j, t);
                t = fork();
                printState(getpid(), pid, i, j, t);
                break;
            }
        }
        if (pid != getpid())
            printState(getpid(), pid, i, j, t);
            break;
    }

and collect the output, ordering them by their self-reported pid numbers (getpid(), not pid variable)

  • Related