假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?
关于stackoverflow的一些类似问题:
(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?
关于Windows的stackoverflow也有类似的问题:
如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程
当前回答
我找到了两个解,都不完美。
1.当收到SIGTERM信号时,通过Kill (-pid)杀死所有子结点。 显然,这个解决方案不能处理“kill -9”,但它确实适用于大多数情况,而且非常简单,因为它不需要记住所有的子进程。
var childProc = require('child_process').spawn('tail', ['-f', '/dev/null'], {stdio:'ignore'});
var counter=0;
setInterval(function(){
console.log('c '+(++counter));
},1000);
if (process.platform.slice(0,3) != 'win') {
function killMeAndChildren() {
/*
* On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually
* the process itself and children.
* On Windows, an JOB object has been applied to current process and children,
* so all children will be terminated if current process dies by anyway.
*/
console.log('kill process group');
process.kill(-process.pid, 'SIGKILL');
}
/*
* When you use "kill pid_of_this_process", this callback will be called
*/
process.on('SIGTERM', function(err){
console.log('SIGTERM');
killMeAndChildren();
});
}
通过同样的方式,如果你调用process,你可以像上面那样安装'exit'处理程序。退出的地方。 注意:Ctrl+C和突然崩溃已经被操作系统自动处理来杀死进程组,这里不再赘述。
2.使用chjj/pty.js生成附加控制终端的进程。 当你以任何方式甚至kill -9终止当前进程时,所有的子进程也会被自动终止(由操作系统?)我猜是因为当前进程占用终端的另一侧,所以如果当前进程死亡,子进程将获得SIGPIPE,因此死亡。
var pty = require('pty.js');
//var term =
pty.spawn('any_child_process', [/*any arguments*/], {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: process.cwd(),
env: process.env
});
/*optionally you can install data handler
term.on('data', function(data) {
process.stdout.write(data);
});
term.write(.....);
*/
其他回答
在POSIX中,exit(), _exit()和_exit()函数被定义为:
如果该进程是控制进程,则SIGHUP信号应发送给控制终端的前台进程组中属于呼叫进程的每个进程。
因此,如果您安排父进程作为其进程组的控制进程,那么当父进程退出时,子进程应该得到一个SIGHUP信号。我不确定当父节点崩溃时是否会发生这种情况,但我认为确实会发生。当然,对于非崩溃的情况,它应该可以正常工作。
请注意,您可能必须阅读大量的小字——包括基本定义(Definitions)部分,以及exit()和setsid()和setpgrp()的系统服务信息——才能了解完整的情况。(我也是!)
受到这里另一个答案的启发,我提出了以下全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)
一些海报已经提到了管道和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)。
我在过去通过在“子”中运行“原始”代码和在“父”中运行“衍生”代码来实现这一点(也就是说:你在fork()之后反转了通常意义上的测试)。然后在“衍生”代码中捕获SIGCHLD…
对你来说可能不行,但管用的时候很可爱。
如果你发送一个信号到pid 0,使用for实例
kill(0, 2); /* SIGINT */
该信号被发送到整个进程组,从而有效地杀死了子进程。
你可以很容易地测试它,比如:
(cat && kill 0) | python
如果你然后按^D,你会看到文本“Terminated”,这表明Python解释器确实已经被杀死,而不是因为stdin被关闭而退出。