Search code examples
cforkshared-memory

Why does one process not reach `exit(0)` when forking?


I have the following program, just to test if I'm understanding forking and shared memory. In summary it makes shared memory, forks, forks, forks, and in each fork, writes data and exits.

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

#include <sys/shm.h>
#include <sys/ipc.h>

int main() {
    
    key_t key = ftok(".", 'x');
    int shmid = shmget(key, sizeof(int)*4, IPC_CREAT | 0666);
    if (shmid < 0) {
        perror("shmget\n");
        return 1;
    }
    int *arr = shmat(shmid, NULL, 0);
    pid_t c1 = fork();
    if (c1==0) {
        pid_t c2 = fork();
        if (c2==0) {
            pid_t c3 = fork();
            if (c3==0) {
                arr[0] = 10;
            } else {
                arr[1] = 11;
            }
            exit(0);
        } else {
            arr[2] = 12;
        }
        exit(0);
    } else {
        arr[3] = 13;
        wait(NULL);
        wait(NULL);
        wait(NULL);

        for (int i=0; i<4; i++) printf("%d ", arr[i]);
        printf("\n");
    }
    exit(0);
}

This prints 0 11 12 13 showing that for some reason the value 10 is never assigned to arr[0].

I expected that every process would reach some call to exit(0). Especially the one after the third fork, I would think should reach the same call to exit(0) that is reached by the second fork.

However, when I add an explicit call to exit inside the third fork's block (i.e. after c3==0), it then executes as expected.

As I write this, it suddenly occurs to me a guess about why this is happening: Is it because the second child reaches exit before the third, and because it exits then the link from the fourth to the parent is broken?


Solution

  • You currently try to wait for three processes in the original parent process but it has only one child, so only the first wait is successful. There's no guarantee that the other processes are ready with their writing to shared memory before you print the result.

    In order to ensure that the children are all done, each parent could wait on its child.

    Example:

    pid_t c1 = fork();
    if (c1==0) { // child
        pid_t c2 = fork();
        if (c2==0) { // child
            pid_t c3 = fork();
            if (c3==0) { // child
                arr[0] = 10;
                exit(0);
            } else {
                arr[1] = 11;
                waitpid(c3, NULL, 0); // waiting for child
            }
            exit(0);
        } else {
            arr[2] = 12;
            waitpid(c2, NULL, 0);    // waiting for child
        }
        exit(0);
    } else {
        arr[3] = 13;
        waitpid(c1, NULL, 0);    // waiting for child
        for (int i=0; i<4; i++) printf("%d ", arr[i]);
        printf("\n");
    }