假设我有一个进程,它正好产生一个子进程。现在,当父进程出于任何原因(正常或异常,通过KILL、^C、Assert Failure或任何其他原因)退出时,我希望子进程死亡。如何正确地做到这一点呢?
有关堆栈溢出的一些类似问题:
- (前面询问)How can I cause a child process to exit when the parent does?
- (稍后询问)Are child processes created with fork() automatically killed when the parent is killed?
关于Windows堆栈溢出的一些类似问题:
23条答案
按热度按时间camsedfj16#
我认为不可能保证只使用标准的POSIX调用。就像现实生活一样,一旦孩子出生,它就有了自己的生活。
父进程**有可能捕获大多数可能的终止事件,并试图在该点上杀伤子进程,但总有一些无法捕获。
例如,任何进程都无法捕获
SIGKILL
。当内核处理此信号时,它将终止指定的进程,而不通知该进程。扩大这个类比--唯一的另一种标准做法是,当孩子发现自己不再有父母时,就会自杀。
对于
prctl(2)
,有一种仅适用于Linux的方法--请参阅其他答案。5sxhfpxr17#
受这里另一个答案的启发,我想出了以下All-POSIX解决方案。一般的想法是在父母和孩子之间创建一个中间过程,这个过程只有一个目的:在父母去世时通知,并明确地杀死孩子。
当无法修改子级中的代码时,此类型的解决方案非常有用。
此方法有两个小注意事项:
顺便说一句,我使用的实际代码是用Python编写的。以下是完整的说明:
vwhgwdsa18#
子进程是否有进出父进程的管道?如果是这样,则在写入时会收到SIGPIPE,或者在读取时会收到EOF--这些情况都可以检测到。
n3h0vuf219#
为了完整起见。在MacOS上,您可以使用kQueue:
hof1towb20#
如果无法修改子进程,可以尝试执行类似以下操作:
这将在启用作业控制的情况下从外壳进程中运行子进程。子进程是在后台产生的。外壳程序等待换行符(或EOF),然后杀死孩子。
当父进程死亡时--无论是什么原因--它将关闭其管道的末端。子外壳将从读取中获得EOF,并继续终止后台的子进程。
xxslljrj21#
在Linux下,您可以在孩子中安装父母死亡信号,例如:
请注意,在fork之前存储父进程id,并在
prctl()
之后在子进程中测试它,可以消除prctl()
和调用子进程的退出之间的争用条件。还要注意,孩子的父母死亡信号在其自己的新创建的孩子中被清除。它不受
execve()
的影响。如果我们确定负责采用所有orphans的系统进程的PID值为1:
但是,依赖于该系统进程是
init
并且具有PID1是不可移植的。POSIX.1-2008规定:调用进程的所有子进程和僵尸进程的父进程ID应设置为实现定义的系统进程的进程ID。也就是说,这些进程将由一个特殊的系统进程继承。
传统上,采用所有孤儿的系统进程是PID1,即init,它是所有进程的祖先。
在像Linux或FreeBSD这样的现代系统上,另一个进程可能具有该角色。例如,在Linux上,进程可以调用
prctl(PR_SET_CHILD_SUBREAPER, 1)
以将其自身建立为继承其任何子体的所有孤儿的系统进程(参见在Fedora 25上的example)。nr7wwzry22#
过去,我通过在“子”中运行“原始”代码和在“父”中运行“衍生”代码(即:在
fork()
之后颠倒通常的测试含义)来实现这一点。然后在“衍生”代码中诱捕SIGCHLD..。在你的情况下可能是不可能的,但当它起作用时很可爱。
falq053o23#
我也在努力解决同样的问题,因为我的程序必须在OS X上运行,所以只支持Linux的解决方案不适用于我。
我得出了与本页面上其他人相同的结论--当父母去世时,没有一种与POSIX兼容的方式来通知孩子。所以我做了一件仅次于最好的事情--让孩子们投票。
当父进程死亡时(无论出于何种原因),子进程的父进程将成为进程1。如果子进程只是定期轮询,它可以检查其父进程是否为1。如果是,则子进程应该退出。
这不是很好,但它很有效,而且比本页面其他地方建议的TCP套接字/锁文件轮询解决方案更容易。