Perl fork和kill - kill(0,$ pid)总是返回1,并且不能杀死孩子

时间:2013-02-21 21:10:02

标签: perl

我正在Linux主机上运行perl脚本。我正在尝试编写一个分叉的脚本,其中孩子启动一个永远需要的程序,父母在5秒后超时,杀死了孩子。这就是我所拥有的:

my $start = time();
my $timeOut = 5;

my $childPid = fork();
if ($childPid) {
    # I am the parent, and $childPid is my child
    while (kill(0, $childPid)) {
        if (time() > $start + $timeOut) {
            $numKilled = kill(SIGTERM, $childPid);
            printf("numKilled: %s\n", $numKilled);
        }
        sleep 1;
    }
}
else {
    # I am the child - do something that blocks forever
    `adb -s 410bf6c1 logcat`;
    exit;
}

输出:

aschirma@graphics9-lnx:~$ perl perl-fork-test.pl 
numKilled: 1
numKilled: 1
numKilled: 1
numKilled: 1
numKilled: 1
...

我期望的行为是我看到“numKilled:1”恰好一次,并且子进程(及其任何子进程)在大约5秒后被杀死。但我从实验中看到,孩子和孩子都没有被杀。 kill(SIGTERM, $childPid)似乎无能为力。

我怎样才能真正杀死孩子?

6 个答案:

答案 0 :(得分:6)

应该是这样的。这不符合最佳做法,但它可以帮助您解决问题......

#!/usr/bin/perl

use warnings;
use strict;
use POSIX qw(:sys_wait_h);

my $timeOut = 5;
$SIG{ALRM} = \&timeout;
$SIG{CHLD} = 'IGNORE',
alarm($timeOut);

my $childPid = fork();
if ($childPid) {
    while(1) {
        print "[$$]: parent...\n"; 
        sleep(2); 
    }
}else {
    # I am the child - do something that blocks forever
    while(1){
        print "[$$]: child...\n";
        sleep(2);
    }
    exit;
}

sub timeout {
    print "killing $childPid\n";
    print "###\n" . `ps -ef | grep -v grep | grep perl` . "###\n";
    if ( ! (waitpid($childPid, WNOHANG)) ) {
        print "killing $childPid...\n";
        kill 9, $childPid;
        die "[$$]: exiting\n";
    }
}

输出:

$ forktest.pl
[24118]: parent...
[24119]: child...
[24118]: parent...
[24119]: child...
[24118]: parent...
[24119]: child...
killing 24119
###
cblack   24118 12548  0 14:12 pts/8    00:00:00 /usr/bin/perl ./forktest.pl
cblack   24119 24118  0 14:12 pts/8    00:00:00 /usr/bin/perl ./forktest.pl
###
killing 24119...
[24118]: exiting

答案 1 :(得分:4)

来自perldoc fork

  

如果你在没有等待孩子的情况下分叉,你就会积累   僵尸。在某些系统上,您可以通过将$ SIG {CHLD}设置为来避免这种情况   “IGNORE”。

$SIG{CHLD} = 'IGNORE';添加到代码顶部时,我能够获得所需的行为。

[ben@imac ~]$ perl test.pl
numKilled: 1
[ben@imac ~]$ 

或者,在waitpid($childPid, 0);之后添加kill也可以。

答案 2 :(得分:1)

您的脚本中usestrictPOSIX,因此SIGTERM被解释为赤字"SIGTERM",这不是&{ #39; t以有用的方式行事。

use strict将此意外裸字变为错误,然后use POSIX提取SIGTERM常量。

答案 3 :(得分:1)

我也看到了这种行为,即使在进程消失之后,kill 0也会返回true;我怀疑你可能会错过对waitpid的调用(通常在SIGCHLD处理程序中完成),这会导致这种情况,但即使添加它之后,kill 0仍然会返回true。

我建议您使用非阻塞waitpid而不是kill(并验证该进程实际上是否死于SIGTERM信号 - 有些可能不会,至少立即)。如果SIGTERM不起作用,您可能还想在一段时间后尝试SIGINT,并在最后的手段SIGKILL。

答案 4 :(得分:1)

尝试终止进程组(使用 - $ pid):

kill TERM => -$pid;

见perlipc。

答案 5 :(得分:0)

我认为问题在于' 0'你正在作为杀死'的第一个参数传递。当我阅读文档时,他们会说' 0' 0只是检查是否可以发送信号到过程,而不发送它。在您的情况下,您想发送' KILL'向子进程发出信号,所以这样做:

kill( 'KILL', $childPid );