0

I am trying to implement some code using AnyEvent and EV. I would like to install a child watcher ( Mockup of what I want done below)

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
}

I would like some help writing the child_alive() as well as figuring out when various callback as well as placing an exit in kill_child(). I need to make sure that cleanup_after_child() is called before the process exits.

Would using an AnyEvent Signal watcher help? My goal is to be able to safely shut down a daemon process, by first killing the processes children and cleaning up after them. I have asked a related question earliyer (Waiting on a child process in perl) and would like to prevent that bug from occuring during the exit handling.

Thanks

EDIT: Would the following work for a child_alive() function?

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

In other words, would the callback have already been called once the child exited, or would the callback only be executed on the next iteration of the EventLoop? if so, How can I exit only after all the signals have been handled? do I just delete all the events like:

$w = undef;
$t = undef;

sorry, I am more of trying to figure out how AnyEvent will handle these signals rather than how signals work in general.

4

2 回答 2

5

You can use a hash to keep track of the children you have.

$children{$pid} = 1;       # On creation.

delete($children{$pid});   # On reaping.

keys(%children)            # Lists existing children.

So all that's left is waiting for all the children to complete, and there's already an example of that in AnyEvent->child's documentation.

All together, we get

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".
于 2013-09-16T16:56:30.663 回答
1

来自perldoc -f kill

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

于 2013-09-16T16:22:34.680 回答