这是我的代码,为了清楚起见,删除了错误处理和其他内容:
sub launch_and_monitor {
my ($script, $timeout) = @_;
sub REAPER {
while ((my $child = waitpid(-1, &WNOHANG)) > 0) {}
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
my $pid = fork;
if (defined $pid) {
if ($pid == 0) {
# in child
monitor($timeout);
}
else {
launch($script);
}
}
}
启动子执行一个shell脚本,然后启动其他进程,如下所示:
sub launch($) {
my ($script) = @_;
my $pid = open(PIPE, "$script|");
# write pid to pidfile
if ($pid != 0) {
while(<PIPE>) {
# do stuff with output
}
close(PIPE) or die $!;
}
}
monitor sub基本上只等待一段指定的时间,然后尝试终止shell脚本。
sub monitor($) {
my ($timeout) = @_;
sleep $timeout;
# check if script is still running and if so get pid from pidfile
if (...) {
my $pid = getpid(...);
kill 9, $pid;
}
}
这会杀死脚本,但是它不会杀死任何子进程。怎么解决?
答案 0 :(得分:12)
如果操作系统支持,则可以使用进程组执行此操作。您需要使脚本流程成为流程组负责人。它运行的子进程将从其父进程继承进程组。然后,您可以使用kill同时向组中的每个进程发送信号。
在launch()
中,您需要将open
行替换为分叉。然后在孩子中,你可以在执行命令之前调用setpgrp()
。以下内容应该有效:
my $pid = open(PIPE, "-|");
if (0 == $pid) {
setpgrp(0, 0);
exec $script;
die "exec failed: $!\n";
}
else {
while(<PIPE>) {
# do stuff with output
}
close(PIPE) or die $!;
}
稍后,要终止脚本进程及其子进程,否定您发出信号的进程ID:
kill 9, -$pid;
答案 1 :(得分:2)
一般来说,我认为你不能指望信号传播到所有子进程中;这不是perl特有的。
也就是说,您可以使用perl kill()内置的 group 进程信号:
...如果SIGNAL是否定的,它会杀死进程组而不是进程......
您可能需要在(直接)子进程中使用setpgrp(),然后将kill调用更改为:
kill -9, $pgrp;
答案 2 :(得分:2)
尝试添加:
use POSIX qw(setsid);
setsid;
位于launch_and_monitor
功能的顶部。这将使您的进程处于单独的会话中,并在会话负责人(即主服务器)退出时导致事务退出。
答案 3 :(得分:0)
杀死一个进程组,但不要忘记父母也可以单独杀死。假设子进程有一个事件循环,他们可以在执行fork()验证之前检查在socketpair中创建的父套接字。实际上,当父套接字消失时,select()会干净地退出,所有需要做的就是检查套接字。
E.g:
use strict; use warnings;
use Socket;
$SIG{CHLD} = sub {};
socketpair(my $p, my $c, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die $!;
print "parent $$, fork 2 kids\n";
for (0..1){
my $kid = fork();
unless($kid){
child_loop($p, $c);
exit;
}
print "parent $$, forked kid $kid\n";
}
print "parent $$, waiting 5s\n";
sleep 5;
print "parent $$ exit, closing sockets\n";
sub child_loop {
my ($p_s, $c_s) = @_;
print "kid: $$\n";
close($c_s);
my $rin = '';
vec($rin, fileno($p_s), 1) = 1;
while(1){
select my $rout = $rin, undef, undef, undef;
if(vec($rout, fileno($p_s), 1)){
print "kid: $$, parent gone, exiting\n";
last;
}
}
}
运行如下:
tim@mint:~$ perl ~/abc.pl
parent 5638, fork 2 kids
parent 5638, forked kid 5639
kid: 5639
parent 5638, forked kid 5640
parent 5638, waiting 5s
kid: 5640
parent 5638 exit, closing sockets
kid: 5640, parent gone, exiting
kid: 5639, parent gone, exiting
tim@mint:~$