我在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)似乎无能为力。

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

评论

您是从某个地方导入SIGTERM常量吗?我敢打赌它的值为0。

您是否正在使用使用警告?使用严格;?为什么不呢?

大家好,所以我发现我看到的问题实际上有所不同-kill是杀死子进程,但不是杀死它的任何子进程。我不想只编辑整个问题,我应该开始一个新的问题吗?

稍微偏离主题,但在对子进程句柄执行任何操作之前,请考虑检查是否(define($ childPid))。 fork会在无法创建进程的情况下返回undefif。

#1 楼

在perldoc分支中:


如果您不等待孩子就进行分支,则会堆积僵尸。在某些系统上,可以通过将$ SIG {CHLD}设置为
“ IGNORE”来避免这种情况。


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

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


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

#2 楼

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

#!/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";
    }
}


输出: >

#3 楼

可能是因为您在脚本中既没有use也没有strictPOSIX,所以SIGTERM被解释为裸字"SIGTERM",它的行为方式并不有用。错误,然后use strict提取use POSIX常数。

评论


或者只是杀死'TERM',$ pid而不用担心常数。

–melpomene
13年2月21日在21:29

请参阅perldoc.perl.org/functions/kill.html:“您也可以在引号中使用信号名称。”

–melpomene
13年2月21日在21:39

我做了您建议的所有操作,但是行为是相同的。 kill(0,$ childPid)仍然不断显示该进程存在,我可以手动确认它确实存在。

–亚当S
13年2月21日在21:49

#4 楼

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

我建议您使用非阻塞waitpid而不是kill(并验证该进程确实确实在SIGTERM信号上死亡-有些可能不会,至少立即消失)。如果SIGTERM无法正常工作,您可能还需要一段时间后再尝试SIGINT,而在最后一种情况下,SIGKILL。

#5 楼

尝试杀死进程组(使用-$ pid):

kill TERM => -$pid;


请参见perlipc。

#6 楼

我认为问题出在作为“ kill”的第一个参数传递的“ 0”。当我阅读文档时,他们说'0'只会检查您是否可以将信号发送到进程,而不发送信号。对于您的情况,您想将'KILL'信号发送给子进程,因此请执行以下操作:

kill( 'KILL', $childPid );


评论


kill(0,$ childPid)基本上是说该进程是否正在运行,然后继续..随后他正在发送SIGTERM

– PodTech.io
17年7月26日在12:40