我使用以下代码等待所有子进程完成:
int pid;
do {
pid = wait(NULL);
} while (pid != -1);
然而,如果我的孙子孙女在他们的父进程死亡的情况下运行,它似乎不起作用。例如,假设我有一个处理孩子Beta的过程Alpha,并且Beta分叉一个孩子Gamma。然后Beta在Gamma继续运行时死亡。我希望能够让Alpha等待Gamma完成。问题在于我无法存储Gamma的进程ID - Alpha需要能够等待所有的孩子,孙子,曾孙等完成而不知道他们的个人pid。可以这样做(如果是这样的话)?
答案 0 :(得分:3)
这不容易做到。
当一个进程退出时,它的子进程被重新分配给进程1(init),因为这是wait()
在任何子进程上唯一可以预期的进程,即使是它不知道的进程 - 其他进程可能不知道如何处理SIGCHLD
,甚至忽略它们没有启动的进程,从而创建了僵尸进程。
没有办法宣称你的过程想要清理任何孙子孙女,而且总是期待清理孩子。
要做的就是让 Beta 进入“等待孩子”状态而不是终止,或者从 Alpha 启动所有子进程。< / p>
答案 1 :(得分:3)
在原始进程中创建pipe,并让所有后代继承管道的写入端。终止后代将隐式关闭其继承的文件描述符。当写入端的最后一个文件描述符关闭时,管道的读取端将立即可读为EOF。
因此,当原始进程准备好等待所有后代的终止时,它会关闭其写入端,然后等待读取端的EOF:
#ifdef FOR_REAL
# include <error-checking.h>
#endif
int p[2];
pipe(p); // Parent
if (! fork()) { // Child
close(p[0]); // Child: close inherited p[0] (not needed)
...
if (! fork()) { // Grandchild
...
exit(0); // Grandchild: inherited p[1] implicitly closed
}
...
exit(0); // Child: inherited p[1] implicitly closed
}
...
// Wait for all descendants to finish
char buf[1];
close(p[1]);
read(p[0], &buf, 1); // Returns 0 (EOF). Could also select(2) or poll(2)
答案 2 :(得分:3)
实际上有一种方法可以优雅地等待孙子孙女。从Linux 3.4开始,有一个名为PR_SET_CHILD_SUBREAPER
的新API,它可以使父进程变成init
。父进程将重新显示其后代中的所有孤儿。
从PRCTL(2)的手册页复制以下内容:
subreaper为其后代履行init(1)的角色 流程。当一个过程变成孤儿时(即它的过程) 直接父母终止)然后该过程将是 归属于最近的仍然生活的祖先subreaper。 随后,在孤立进程中调用getppid()将会 现在返回subreaper进程的PID,并且当 孤儿终止,它将是subreaper进程 接收SIGCHLD信号,并且能够等待(2) 发现终止状态的过程。