假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
当前回答
以防它与任何人相关,当我从c++派生子进程中的JVM实例时,我可以让JVM实例在父进程完成后正确终止的唯一方法是执行以下操作。如果这不是最好的方法,希望有人能在评论中提供反馈。
1)在通过execv启动Java应用程序之前,在fork子进程上调用prctl(PR_SET_PDEATHSIG, SIGHUP)
2)在Java应用程序中添加一个shutdown钩子,轮询直到其父PID等于1,然后执行一个硬的Runtime.getRuntime().halt(0)。轮询是通过启动运行ps命令的单独shell来完成的(参见:如何在Linux上的Java或JRuby中找到我的PID ?)
130118年编辑:
这似乎不是一个可靠的解决方案。我仍然在努力理解发生的事情的细微差别,但在屏幕/SSH会话中运行这些应用程序时,有时仍然会遇到孤立的JVM进程。
Instead of polling for the PPID in the Java app, I simply had the shutdown hook perform cleanup followed by a hard halt as above. Then I made sure to invoke waitpid in the C++ parent app on the spawned child process when it was time to terminate everything. This seems to be a more robust solution, as the child process ensures that it terminates, while the parent uses existing references to make sure that its children terminate. Compare this to the previous solution which had the parent process terminate whenever it pleased, and had the children try to figure out if they had been orphaned before terminating.
其他回答
我把父pid用环境传递给子pid, 然后定期检查子进程中是否存在/proc/$ppid。
在Linux下,你可以在子进程中安装父进程死亡信号,例如:
#include <sys/prctl.h> // prctl(), PR_SET_PDEATHSIG
#include <signal.h> // signals
#include <unistd.h> // fork()
#include <stdio.h> // perror()
// ...
pid_t ppid_before_fork = getpid();
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() != ppid_before_fork)
exit(1);
// continue child execution ...
请注意,在fork之前存储父进程id,并在prctl()之后在子进程中测试它,消除了prctl()和调用子进程的退出之间的竞争条件。
还要注意,子进程的父进程死亡信号在新创建的子进程中被清除。它不受execve()的影响。
如果我们确定负责收养所有孤儿的系统进程PID为1,这个测试就可以简化:
pid_t pid = fork();
if (pid == -1) { perror(0); exit(1); }
if (pid) {
; // continue parent execution
} else {
int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
if (r == -1) { perror(0); exit(1); }
// test in case the original parent exited just
// before the prctl() call
if (getppid() == 1)
exit(1);
// continue child execution ...
但是,依赖于系统进程的初始化和PID 1是不可移植的。posix . 1的授权- 2008指定:
调用进程的所有现有子进程和僵尸进程的父进程ID应设置为实现定义的系统进程的进程ID。也就是说,这些进程应该由一个特殊的系统进程继承。
传统上,采用所有孤儿进程的系统进程是PID 1,即init -它是所有进程的祖先。
在像Linux或FreeBSD这样的现代系统上,另一个进程可能具有这个角色。例如,在Linux上,一个进程可以调用prctl(PR_SET_CHILD_SUBREAPER, 1)来将自己建立为继承其任何后代的所有孤儿的系统进程(参见Fedora 25上的一个例子)。
通过在prctl()系统调用中指定PR_SET_PDEATHSIG选项,子进程可以要求内核在父进程死亡时传递SIGHUP(或其他信号),就像这样:
prctl(PR_SET_PDEATHSIG, SIGHUP);
详见man 2 prctl。
编辑:这是linux专用的
另一种Linux特有的方法是在一个新的PID名称空间中创建父进程。然后它将是该名称空间中的PID 1,当它退出时,它的所有子节点将立即被SIGKILL杀死。
不幸的是,为了创建一个新的PID名称空间,您必须拥有CAP_SYS_ADMIN。但是,这种方法非常有效,在初始启动父进程之后不需要对父进程或子进程进行任何实际更改。
请参见clone(2)、pid_namespaces(7)和unshare(2)。
一些海报已经提到了管道和kqueue。事实上,你也可以通过socketpair()调用创建一对连接的Unix域套接字。套接字类型应该是SOCK_STREAM。
Let us suppose you have the two socket file descriptors fd1, fd2. Now fork() to create the child process, which will inherit the fds. In the parent you close fd2 and in the child you close fd1. Now each process can poll() the remaining open fd on its own end for the POLLIN event. As long as each side doesn't explicitly close() its fd during normal lifetime, you can be fairly sure that a POLLHUP flag should indicate the other's termination (no matter clean or not). Upon notified of this event, the child can decide what to do (e.g. to die).
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
int sv[2]; /* sv[0] for parent, sv[1] for child */
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
pid_t pid = fork();
if ( pid > 0 ) { /* parent */
close(sv[1]);
fprintf(stderr, "parent: pid = %d\n", getpid());
sleep(100);
exit(0);
} else { /* child */
close(sv[0]);
fprintf(stderr, "child: pid = %d\n", getpid());
struct pollfd mon;
mon.fd = sv[1];
mon.events = POLLIN;
poll(&mon, 1, -1);
if ( mon.revents & POLLHUP )
fprintf(stderr, "child: parent hung up\n");
exit(0);
}
}
您可以尝试编译上面的概念验证代码,并在./a这样的终端中运行它。&。你有大约100秒的时间来尝试通过各种信号杀死父PID,否则它就会退出。在任何一种情况下,您都应该看到消息“child: parent hung up”。
与使用SIGPIPE处理程序的方法相比,该方法不需要尝试write()调用。
这种方法也是对称的,即进程可以使用相同的通道来监视彼此的存在。
这个解决方案只调用POSIX函数。我在Linux和FreeBSD中尝试了这个方法。我认为它应该在其他unix上工作,但我还没有真正测试过。
参见:
Linux手册中的unix(7), FreeBSD的unix(4), poll(2), socketpair(2), Linux上的socket(7)。