难以理解fork()和进程树

时间:2018-10-08 22:18:28

标签: c process operating-system fork

基于以下代码,我一直在尝试制作进程树。起初,我以为它类似于对称的二叉树,但不再这样。

#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;
}

此外,运行此代码时,输​​出的数量是我最初预测的数量的2倍。例如,d被打印了16次,但我认为它只会被打印8次。任何更深入的解释都将有所帮助。

1 个答案:

答案 0 :(得分:3)

为帮助您自己理解,请在每个printf()语句中包含PID和PPID。同样,捕获并报告wait()每次调用时返回的值(死子PID);您可以决定是否报告status,但是如果wait()调用由于没有子项而失败,则将其初始化为零。通常,孩子会在父母从wait()调用返回之前继续。

或者更好的是编写一个日志记录功能,可以为您处理详细信息。 例如,考虑以下代码:

#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;
}

示例运行为我提供了

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

您可以逐一浏览数据,看看有4个fork()调用有2 4 个进程(每个调用之后,有2个进程之前有1个进程,因此在一个调用之后,有2个进程;在2个调用之后,有4个进程,依此类推),并且子进程在父进程继续之前退出(因此,在此示例中,PID 7357是最后打印{{1 }}标签,a标签,b标签和c标签)。

每当您在跟踪过程树时遇到困难时,请使用与此类似的打印技术来帮助您了解正在发生的事情。