Search code examples
cforkpid

Need help about 2 particular things about processIDs in forking (on unix like system) - C language


Edit: Well, my brain apparently autocompleted pid[1] to pid[0]. So the first question is answered by me being blind.

We were given following code example by university, I added a print call to better understand what is actually happening, with the REAL return values of everything.

I thought I understand how forking and assigning processIDs works. But I don't.

I don't understand two particular things:

  1. Why does the processID (getpid()) of the main process equal the return value of fork() inside the main process? It says fork returns the pid of the CHILD. And getpid() returns the pid of the CALLING process, it should not be equal by that logic.
  2. Why is pid0 and pid2 zero in the last routine, or to be exact, which one of both fork() calls was called again for the last fork? I assume one was cloned the other represents the return value of the last fork().

I am new to Stackoverflow, thank you in advance for any help!

int v[3] = {0, 0, 0};

void handler(int sig) {
    v[0]++;
}


 int main() {

    int pid[3];

    signal(SIGUSR1, handler);
    printf("Main Process ID: %d\n", getpid());

    pid[0] = fork();
    pid[1] = getpid();
    pid[2] = fork();

    if (pid[0] == pid[2]) {
        v[1]++;
        sleep(4);
    } else if (pid[1] == getppid()) {
        sleep(3);
        v[2]++;
        sleep(2);
    } else if (pid[1] == getpid()) {
        sleep(1);
        v[2]++;
        kill(pid[2], SIGUSR1);
        wait(NULL);
        if (pid[0] > 0) {
            v[1]++;
            wait(NULL);
        }
    }

    printf("ppid %d, processID %d, pid0 %d, pid1 %d, pid2 %d\n", getppid(), getpid(), pid[0], pid[1], pid[2]);
    printf("%d %d %d\n", v[0], v[1], v[2]);
}
Result:
Main Process ID: 286161
// 2. Fork (I assume)
ppid 286162, processID 286164, pid0 0, pid1 286162, pid2 0
1 1 0
// 2. Fork (I assume)
ppid 286161, processID 286162, pid0 0, pid1 286162, pid2 286164
0 0 1
// 1. Fork (I assume)
ppid 286161, processID 286163, pid0 286162, pid1 286161, pid2 0
1 0 1
// Main Process
ppid 154332, processID 286161, pid0 286162, pid1 286161, pid2 286163
0 1 1

Solution

  • Fork returns 0 to the child and the PID of the child to the parent.

    First, let's look at this:

        pid[0] = fork(); // <-- forks child_1. Returns PID(child_1) in parent, 0 in child_1
        pid[1] = getpid(); // <-- Returns PID(parent) in parent, PID(child_1) in child_1
        pid[2] = fork(); // <-- Forks child_2 from parent, forks child_3 from child_1. Returns PID(child_2) in parent, 0 in child_2, PID(child_3) in child_1, 0 in child_3
    

    The first fork spawns one child process, off of the parent. When fork is called the second time, there are two copies of the program running, so two processes are created: one from the original parent (ie. a sibling of child_1), and another from the first child (a grand-child of the original parent).

    So for this:

    1) ppid 286162, processID 286164, pid0 0, pid1 286162, pid2 0
    2) ppid 286161, processID 286162, pid0 0, pid1 286162, pid2 286164
    3) ppid 286161, processID 286163, pid0 286162, pid1 286161, pid2 0
    4) ppid 154332, processID 286161, pid0 286162, pid1 286161, pid2 286163
    

    I'll give each line a name: (1) is child_3, (2) is child_1, (3) is child_2, and (4) is the parent.

    It's easier if you draw it out and step through it line-by-line.

    Start with this:

                SHELL
                  |
                Parent
    

    Then call fork:

                SHELL
                  |
                Parent
                /
          child_1
    

    In parent: PID[0] = pid_of(child_1)
    In child_1: PID[0] = 0

    Next, the call to getpid.

    In Parent: PID[1] = pid_of(parent)
    In child_1: PID[1] = pid_of(child_1)

    then the second fork:

                SHELL
                  |
                Parent
                /    \
          child_1    child_2
             |
          child_3
    

    In parent: PID[2] = pid_of(child_2)
    In child_1: PID[2] = pid_of(child_3)
    In child_2: PID[0] and PID[1] are inherited from parent, so PID[0]=pid_of(child_1), PID[1] = pid_of(parent), and PID[2] = 0
    in child_3: PID[0] and PID[1] are inherited from child_1, so PID[0] = 0, PID[1] = pid_of(child_1), and PID[2] = 0