假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
当前回答
正如其他人指出的那样,当父进程退出时,依赖父进程pid变成1是不可移植的。不需要等待特定的父进程ID,只需要等待ID发生变化:
pit_t pid = getpid();
switch (fork())
{
case -1:
{
abort(); /* or whatever... */
}
default:
{
/* parent */
exit(0);
}
case 0:
{
/* child */
/* ... */
}
}
/* Wait for parent to exit */
while (getppid() != pid)
;
如果不想以全速轮询,可以根据需要添加微睡眠。
在我看来,这个选项比使用管道或依赖于信号更简单。
其他回答
我认为不可能保证只使用标准POSIX调用。就像现实生活一样,一旦孩子被孕育出来,它就有了自己的生命。
父进程可以捕获大多数可能的终止事件,并尝试在此时终止子进程,但总有一些无法捕获。
例如,没有进程可以捕获SIGKILL。当内核处理这个信号时,它将杀死指定的进程,而不通知该进程。
扩展一下类比——唯一的另一种标准方式是,当孩子发现自己不再有父母时自杀。
使用prctl(2)有一种linux独有的方法——请参阅其他答案。
正如其他人指出的那样,当父进程退出时,依赖父进程pid变成1是不可移植的。不需要等待特定的父进程ID,只需要等待ID发生变化:
pit_t pid = getpid();
switch (fork())
{
case -1:
{
abort(); /* or whatever... */
}
default:
{
/* parent */
exit(0);
}
case 0:
{
/* child */
/* ... */
}
}
/* Wait for parent to exit */
while (getppid() != pid)
;
如果不想以全速轮询,可以根据需要添加微睡眠。
在我看来,这个选项比使用管道或依赖于信号更简单。
受到这里另一个答案的启发,我提出了以下全posix解决方案。一般思想是在父进程和子进程之间创建一个中间进程,其目的只有一个:注意父进程何时死亡,并显式地终止子进程。
当子进程中的代码无法修改时,这种解决方案非常有用。
int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
close(p[1]); // close write end of pipe
setpgid(0, 0); // prevent ^C in parent from stopping this process
child = fork();
if (child == 0) {
close(p[0]); // close read end of pipe (don't need it here)
exec(...child process here...);
exit(1);
}
read(p[0], 1); // returns when parent exits for any reason
kill(child, 9);
exit(1);
}
使用这种方法有两个小注意事项:
如果你故意杀死中间进程,那么当父进程死亡时,子进程不会被杀死。 如果子进程在父进程之前退出,那么中间进程将尝试杀死原来的子进程pid,该进程现在可以引用一个不同的进程。(这可以通过在中间过程中编写更多代码来解决。)
顺便说一句,我使用的实际代码是Python的。为了完整起见,这里是:
def run(*args):
(r, w) = os.pipe()
child = os.fork()
if child == 0:
os.close(w)
os.setpgid(0, 0)
child = os.fork()
if child == 0:
os.close(r)
os.execl(args[0], *args)
os._exit(1)
os.read(r, 1)
os.kill(child, 9)
os._exit(1)
os.close(r)
安装一个陷阱处理程序来捕获SIGINT,如果你的子进程还活着,它就会杀死它,尽管其他的帖子是正确的,它不会捕获SIGKILL。
以独占访问的方式打开一个.lockfile,并让子进程尝试打开它——如果打开成功,子进程应该退出
我认为一种快速而又不那么简单的方法是在子节点和父节点之间创建一个管道。当父节点退出时,子节点将收到一个SIGPIPE。