假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
当前回答
如果你无法修改子进程,你可以尝试以下方法:
int pipes[2];
pipe(pipes)
if (fork() == 0) {
close(pipes[1]); /* Close the writer end in the child*/
dup2(pipes[0], STDIN_FILENO); /* Use reader end as stdin (fixed per maxschlepzig */
exec("sh -c 'set -o monitor; child_process & read dummy; kill %1'")
}
close(pipes[0]); /* Close the reader end in the parent */
这将在启用作业控制的shell进程中运行子进程。子进程在后台生成。shell等待换行符(或EOF),然后终止子进程。
当父进程死亡时——不管是什么原因——它将关闭管道的一端。子shell将从read中获得一个EOF,并继续杀死后台的子进程。
其他回答
一些海报已经提到了管道和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)。
以防它与任何人相关,当我从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.
我认为不可能保证只使用标准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)
;
如果不想以全速轮询,可以根据需要添加微睡眠。
在我看来,这个选项比使用管道或依赖于信号更简单。
Historically, from UNIX v7, the process system has detected orphanity of processes by checking a process' parent id. As I say, historically, the init(8) system process is a special process by only one reason: It cannot die. It cannot die because the kernel algorithm to deal with assigning a new parent process id, depends on this fact. when a process executes its exit(2) call (by means of a process system call or by external task as sending it a signal or the like) the kernel reassigns all children of this process the id of the init process as their parent process id. This leads to the most easy test, and most portable way of knowing if a process has got orphan. Just check the result of the getppid(2) system call and if it is the process id of the init(2) process then the process got orphan before the system call.
这种方法会产生两个问题:
first, we have the possibility of changing the init process to any user process, so How can we assure that the init process will always be parent of all orphan processes? Well, in the exit system call code there's a explicit check to see if the process executing the call is the init process (the process with pid equal to 1) and if that's the case, the kernel panics (It should not be able anymore to maintain the process hierarchy) so it is not permitted for the init process to do an exit(2) call. second, there's a race condition in the basic test exposed above. Init process' id is assumed historically to be 1, but that's not warranted by the POSIX approach, that states (as exposed in other response) that only a system's process id is reserved for that purpose. Almost no posix implementation does this, and you can assume in original unix derived systems that having 1 as response of getppid(2) system call is enough to assume the process is orphan. Another way to check is to make a getppid(2) just after the fork and compare that value with the result of a new call. This simply doesn't work in all cases, as both call are not atomic together, and the parent process can die after the fork(2) and before the first getppid(2) system call. The processparent id only changes once, when its parent does anexit(2)call, so this should be enough to check if thegetppid(2)result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children ofinit(8)`, but you can assume safely these processes as having no parent either (except when you substitute in a system the init process)