【问题标题】:How to check if a child process is alive when using a child watcher in Perl在 Perl 中使用子观察程序时如何检查子进程是否处于活动状态
【发布时间】:2013-09-17 09:20:24
【问题描述】:

我正在尝试使用 AnyEvent 和 EV 实现一些代码。 我想安装一个儿童观察者(我想在下面做的样机)

use Modern::Perl;
use AnyEvent;
use EV;

my $SIG{QUIT} = \&kill_child;
my $exit_status = undef;
my $pid = fork or exec 'child.pl';
my $w = AnyEvent->child(pid => $pid, cb => sub { $exit_status = $_[2]; cleanup_after_child(); });
my $t = AE::timer 0, 5, sub { say $exit_status; fork_another_process(); };

AnyEvent->condvar->recv;

sub kill_child{
    foreach my $signal (qw(HUP QUIT INT KILL)){
        if(child_alive()){
            kill($signal,$pid);
            sleep 1;
        }
    }
    exit; #Probably wrong
}

我需要一些帮助来编写 child_alive() 以及找出各种回调的时间以及在 kill_child() 中放置一个出口。我需要确保在进程退出之前调用 cleanup_after_child()。

使用 AnyEvent 信号观察器会有帮助吗?我的目标是能够安全地关闭守护进程,首先杀死进程子进程并在它们之后进行清理。我之前曾问过一个相关问题 (Waiting on a child process in perl),并希望防止在退出处理期间发生该错误。

谢谢

编辑:以下内容是否适用于 child_alive() 函数?

sub child_alive{
    return defined($exit_status)?1:0;
}

换句话说,一旦孩子退出,回调是否已经被调用,或者回调只会在 EventLoop 的下一次迭代中执行?如果是这样,只有在处理完所有信号后我才能退出?我是否只是删除所有事件,例如:

$w = undef;
$t = undef;

抱歉,我更想弄清楚 AnyEvent 将如何处理这些信号,而不是信号的一般工作方式。

【问题讨论】:

    标签: perl events unix posix anyevent


    【解决方案1】:

    您可以使用哈希来跟踪您拥有的孩子。

    $children{$pid} = 1;       # On creation.
    
    delete($children{$pid});   # On reaping.
    
    keys(%children)            # Lists existing children.
    

    所以剩下的就是等待所有孩子完成,AnyEvent->child 的文档中已经有一个例子。

    我们在一起,我们得到了

    my $done = AnyEvent->condvar;
    my %children;
    my $spawner;
    
    sub ae_sleep {
       my ($dur) = @_;
       my $done = AnyEvent->condvar;
       my $t = AnyEvent->timer(after => $dur, cb => sub { $done->send });
       $done->recv;
    }
    
    sub kill_children {
       for my $sig (qw( HUP QUIT INT KILL )) {
          last if !%children;
          kill($sig => $_) for keys(%children);
          ae_sleep(1);
       }
    }
    
    $SIG{QUIT} = sub {
       $spawner = undef;   # Stop creating new children.
       kill_children();    # Kill existing children.
       $done->recv;        # Wait for children to be reaped.
       exit(0);
    }
    

    sub create_child {
       my $pid = ...;
       $done->begin;
       $children{$pid} = AnyEvent->child(
          pid => $pid,
          cb  => sub {
             my ($pid, $status) = @_;
             delete($children{$pid});
             warn "pid $pid exited with status $status";
             $done->end;
          },
       );
    }
    
    $spawner = AnyEvent->timer(
       after    => 0,
       interval => 5,
       cb       => \&create_child.
    );
    
    AnyEvent->condvar->recv; # Wait "for ever".
    

    【讨论】:

    • 那么,$done->end 在这里做了什么?如果我有 2 个进程,我是否需要有 2 个 $done 变量,或者它是否足够聪明而不会退出。有一个计时器怎么样,我需要先删除它吗?我在我的问题中添加了更多内容,谢谢。
    • 那么,假设我有一个 AE::timer 来轮询新的工作,那么当所有的孩子都完成后,删除它会本质上使脚本退出吗?另外,我仍在试图弄清楚回调何时实际运行(如果我杀死了孩子,何时会调用回调?立即,还是仅在事件循环的下一次迭代中?)
    • 这里没有话题。在检查事件之前不会发生。
    • 将计时器和信号处理程序集成到答案中。
    【解决方案2】:

    来自perldoc -f kill

    如果 SIGNAL 是数字 0 或字符串“ZERO”(或“SIGZZERO”),则否 信号被发送到进程,但“kill”检查是否可以发送 给它的一个信号(简而言之,这意味着该进程由同一进程拥有 用户,或者我们是超级用户)。这对于检查子进程很有用 仍然活着(即使只是作为僵尸)并且没有更改其 UID。看 perlport 以获取有关此构造可移植性的说明。

    【讨论】:

    • 谢谢,这确实解决了一个问题。但我仍在试图弄清楚 AnyEvent 在这种情况下将如何工作。主要是,我如何在确保所有回调/信号都得到处理的同时退出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多