在perl中,当使用open创建子项时杀死孩子及其子女

时间:2009-11-04 18:29:41

标签: perl kill children

这是我的代码,为了清楚起见,删除了错误处理和其他内容:

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;
    }
}

这会杀死脚本,但是它不会杀死任何子进程。怎么解决?

4 个答案:

答案 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:~$