nice_remark
nice_remark

Reputation: 337

Trouble understanding fork() and process trees

Based on the code below, I have been trying to make a process tree. At first I was thinking it would be similar to a binary tree that is symmetric, but no longer think so.

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

int main() {
    int status;
    pid_t i;

    i = fork();
    wait(&status);
    printf("a; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("b; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("c; i = %d\n", i);

    i = fork();
    wait(&status);
    printf("d; i = %d\n", i);

    return 0;
}

Also, when running this code, there are 2x as many output as I have originally predicted. For example, d is printed out 16 times, but I figured it would only be printed out 8 times. Any deeper explanation would be helpful.

Upvotes: 1

Views: 89

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753475

To help yourself understand, include PID and PPID in each printf() statement. Also, capture and report the value (dead child PID) returned by wait() each time it is called; you can decide whether to report status or not, but initialize it to zero in case (for when) the wait() calls fail because there is no child. The children continue before the parent returns from the wait() calls, in general.

Or, better, write a logging function that can handle the details for you. For example, consider this code:

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

static void print_info(const char *tag, int corpse, int status, int child)
{
    int pid = getpid();
    int ppid = getppid();
    printf("PID %5d, PPID %5d: %s (corpse: %5d, status 0x%.4X), child %5d\n",
            pid, ppid, tag, corpse, status, child);
}

int main(void)
{
    int pid = getpid();
    int ppid = getppid();
    printf("Initial PID %5d, PPID %5d:\n", pid, ppid);

    int status = 0;
    int child = fork();
    int corpse = wait(&status);
    print_info("a", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("b", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("c", corpse, status, child);

    child = fork();
    corpse = wait(&status);
    print_info("d", corpse, status, child);

    return 0;
}

A sample run gives me:

Initial PID  7357, PPID 20754:
PID  7358, PPID  7357: a (corpse:    -1, status 0x0000), child     0
PID  7359, PPID  7358: b (corpse:    -1, status 0x0000), child     0
PID  7360, PPID  7359: c (corpse:    -1, status 0x0000), child     0
PID  7361, PPID  7360: d (corpse:    -1, status 0x0000), child     0
PID  7360, PPID  7359: d (corpse:  7361, status 0x0000), child  7361
PID  7359, PPID  7358: c (corpse:  7360, status 0x0000), child  7360
PID  7362, PPID  7359: d (corpse:    -1, status 0x0000), child     0
PID  7359, PPID  7358: d (corpse:  7362, status 0x0000), child  7362
PID  7358, PPID  7357: b (corpse:  7359, status 0x0000), child  7359
PID  7363, PPID  7358: c (corpse:    -1, status 0x0000), child     0
PID  7364, PPID  7363: d (corpse:    -1, status 0x0000), child     0
PID  7363, PPID  7358: d (corpse:  7364, status 0x0000), child  7364
PID  7358, PPID  7357: c (corpse:  7363, status 0x0000), child  7363
PID  7365, PPID  7358: d (corpse:    -1, status 0x0000), child     0
PID  7358, PPID  7357: d (corpse:  7365, status 0x0000), child  7365
PID  7357, PPID 20754: a (corpse:  7358, status 0x0000), child  7358
PID  7366, PPID  7357: b (corpse:    -1, status 0x0000), child     0
PID  7367, PPID  7366: c (corpse:    -1, status 0x0000), child     0
PID  7368, PPID  7367: d (corpse:    -1, status 0x0000), child     0
PID  7367, PPID  7366: d (corpse:  7368, status 0x0000), child  7368
PID  7366, PPID  7357: c (corpse:  7367, status 0x0000), child  7367
PID  7369, PPID  7366: d (corpse:    -1, status 0x0000), child     0
PID  7366, PPID  7357: d (corpse:  7369, status 0x0000), child  7369
PID  7357, PPID 20754: b (corpse:  7366, status 0x0000), child  7366
PID  7370, PPID  7357: c (corpse:    -1, status 0x0000), child     0
PID  7371, PPID  7370: d (corpse:    -1, status 0x0000), child     0
PID  7370, PPID  7357: d (corpse:  7371, status 0x0000), child  7371
PID  7357, PPID 20754: c (corpse:  7370, status 0x0000), child  7370
PID  7372, PPID  7357: d (corpse:    -1, status 0x0000), child     0
PID  7357, PPID 20754: d (corpse:  7372, status 0x0000), child  7372

You can chase through the data, seeing that there are 24 processes for the 4 fork() calls (after each call, there are 2 processes where there was 1 process before, so after one call there are 2 processes; after 2 calls, 4 processes; and so on), and that the child process exits before the parent gets to continue (so, in this example, PID 7357 is the last to print the a tag, and the b tag, and the c tag, and the d tag).

Any time you're having difficulty tracing the process tree, use a printing technique similar to this to help you understand what is going on better.

Upvotes: 3

Related Questions