C 程序产生两个进程树而不是一个

C program yields two process trees instead of one

如果我的解释是正确的,从下面的C程序中期望获得一棵进程树,其中从一个父进程生成三个子进程和两个孙进程:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int num;
    pid_t t;
    for (num = 0; num < 2; num++) {
        t = fork();
        if (t == 0)
        break;
    }
    t = fork();
    printf("I am the process %d and my father is %d\n", getpid(), getppid());
    return 0;
}

但是编译执行后,输出结果如下:

I am the process 2133 and my father is 2127
I am the process 2134 and my father is 2133
I am the process 2137 and my father is 778
I am the process 2135 and my father is 778
I am the process 2138 and my father is 778
I am the process 2136 and my father is 778

正如预期的那样,产生了六个进程,但其中四个是由进程 #778 产生的。另一方面,其中只有一个 (#2134) 似乎是由初始过程 (#2133) 产生的。总共生成了两个过程树,而不是一个。

为什么会出现这种行为?是不是说上面提到的四个进程已经被#778进程adopted/reclaimed了?

作为 , you're not waiting for the children to finish in the parent,因此孤立进程在 parents 退出后被 shell 回收。

正确的树结构是:

   1
   |
   2
 / | \
3  4  5
|  |
6  7

其中:

  • 1 是 shell
  • 2是你的主程序;它在循环中产生 3 和 4,在循环后产生 5
  • 3 和 4 是由 2 在循环中产生的 children;每个在 break
  • 上存在循环后产生 1 个新的 child(6 和 7)
  • 5 在循环之后生成,因此是叶进程
  • 6 和 7 是 3 和 4 循环后产生的叶进程

这里是 cleaned-up 代码,可以提供可理解的输出:

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

int main() {
    printf("original process is %d\n", getpid());

    for (int num = 0; num < 2; num++) {
        if (fork() == 0) { // 0 means child
            break;
        }
    }

    fork(); // fork after the loop
    printf("I am %d and my parent is %d\n", getpid(), getppid());

    while (wait(NULL) > 0) {} // wait for each child process

    return 0;
}

示例输出:

original process is 540
I am 540 and my parent is 1
I am 541 and my parent is 540
I am 543 and my parent is 541
I am 542 and my parent is 540
I am 544 and my parent is 540
I am 545 and my parent is 542

我已经执行了你的代码,通过首先打印根进程的 pid,获得了这个输出:

$ pru
start: pid == 6024
I am the process 6025 and my father is 6024
I am the process 6024 and my father is 5524
I am the process 6027 and my father is 6025
I am the process 6028 and my father is 6024
I am the process 6026 and my father is 6024
I am the process 6029 and my father is 6026

我可以重新排序数据以构建树,从进程开始 6024:

I am the process 6024 and my father is 5524
  I am the process 6026 and my father is 6024
    I am the process 6029 and my father is 6026
  I am the process 6025 and my father is 6024
    I am the process 6027 and my father is 6025
  I am the process 6028 and my father is 6024

你也可以这样做,通过查看进程 6024 显示完全不同的 parent(在本例中为 5524),它不在进程列表中。

在你的情况下,输出是错误的(或者你修改了它或者发布的代码与产生输出的代码不同)因为过程树不是这里显示的。

  • process 6024 执行完整循环,因此,执行三个 fork()s,创建树更多进程(两个在循环中,一个在循环之后)。
  • 其中两个进程(在循环中创建的:60256026break循环,并在循环外执行fork(),创建每个进程一个,通过调用循环外的 fork() 。为您提供进程 60276029,分别为

你显示的输出,不能排列在这个树结构中,如果我们对行进行排序:

I am the process 2133 and my father is 2127
  I am the process 2134 and my father is 2133
I am the process 2137 and my father is 778
I am the process 2135 and my father is 778
I am the process 2138 and my father is 778
I am the process 2136 and my father is 778

因为只有进程 2134 是其他进程的 child... 而不是树。

备注

我在您的问题的评论中看到,出于某种原因,children 已被重新parent 为 shell。在 UNIX 中,没有进程被重新parent 到另一个 parent,除非它是 parent exit()s *无论如何),在这种情况下,它们被重新parent重新处理为 PID=1(initsystemd 进程,这就是 pid 1 的特殊之处)所以你的进程可以重新parented,但绝不会与 PID=1 的进程不同。事实上,你可以通过测试 getppid() 的结果来检查你的 parent 是否已经死亡,如果它是 returns 1,那么你的 parent 就没有存活不再。

最后,有很多关于此模式和 unix 进程结构的文档,但如果我不得不推荐您阅读它,我应该推荐您参考 POSIX 规范,可从 The Open Group