【问题标题】:Perl fork and kill - kill(0, $pid) always returns 1, and can't kill the childPerl fork and kill - kill(0, $pid) 总是返回 1,并且不能杀死孩子
【发布时间】:2013-02-21 21:10:01
【问题描述】:

我在 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。
  • 你在使用use warnings; use strict;吗?为什么不呢?
  • 嘿伙计们,所以我发现我看到的问题实际上有点不同 - kill 正在杀死子进程而不是它的任何子进程。我不想只编辑整个问题,我应该开始一个新问题吗?
  • 有点题外话,但在对您的子进程句柄进行任何操作之前,请考虑检查if ( defined( $childPid ) )。 forkreturns undef如果创建进程失败。

标签: perl


【解决方案1】:

应该是这样的。这没有遵循最佳实践,但它应该可以帮助您解决问题...

#!/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

【讨论】:

    【解决方案2】:

    来自perldoc fork:

    如果你不等孩子就分叉,你会积累 僵尸。在某些系统上,您可以通过将 $SIG{CHLD} 设置为 “忽略”。

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

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

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

    【讨论】:

      【解决方案3】:

      您的脚本中很可能use既不是strict也不是POSIX,因此SIGTERM被解释为裸词"SIGTERM",它的行为方式没有用处。

      use strict 将这个意外的裸词变成错误,然后 use POSIX 拉入 SIGTERM 常量。

      【讨论】:

      • 或者只做kill 'TERM', $pid,不要为常量烦恼。
      • 参见perldoc.perl.org/functions/kill.html:“您也可以在引号中使用信号名称。”
      • 我做了你建议的一切,但行为是一样的。 kill(0, $childPid) 仍然不断显示该进程存在,我可以手动确认它确实存在。
      【解决方案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
          猜你喜欢
          • 1970-01-01
          • 2015-01-24
          • 2023-03-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多