在退出之前,在perl中杀死子进程的正确方法是什么?

时间:2010-03-21 16:43:24

标签: perl fork irc poe

我正在运行一个IRC Bot(Bot::BasicBot),它有两个运行File::Tail的子进程但是在退出时它们不会终止。所以我在退出前使用Proc::ProcessTable像这样杀死他们:

my $parent=$$;
my $proc_table=Proc::ProcessTable->new();
for my $proc (@{$proc_table->table()}) {
  kill(15, $proc->pid) if ($proc->ppid == $parent);
}

它有效,但我收到了这个警告:

14045: !!! Child process PID:14047 reaped:
14045: !!! Child process PID:14048 reaped:
14045: !!! Your program may not be using sig_child() to reap processes.
14045: !!! In extreme cases, your program can force a system reboot
14045: !!! if this resource leakage is not corrected.

我还能做些什么来杀死子进程?分叉流程是使用forkit中的Bot::BasicBot方法创建的。

示例脚本:

package main;

my $bot = SOMEBOT->new ( server => 'irc.dal.net', channels => ['#anemptychannel'] );

$SIG{'INT'} = 'Handler';
$SIG{'TERM'} = 'Handler';

sub Handler {
$bot->_stop('Leaving.');
}

$bot->run;

package SOMEBOT;

use base qw(Bot::BasicBot);
use File::Tail;
use Proc::ProcessTable;
sub irc_error_state { die if $_[10] =~ /Leaving\./; }
sub help { return; }


sub stop_state {
my $parent=$$;
my $proc_table=Proc::ProcessTable->new();
for my $proc (@{$proc_table->table()}) {
  kill(15, $proc->pid) if ($proc->ppid == $parent);
}
die;

}

sub connected {
my $self = shift;

$self->forkit (
                run     =>  \&announcer,
                body    =>  '/home/somebody/somefile.txt',
                channel =>  '#anemptychannel',
              ) unless $self->{log1};
$self->{log1} = 1;


$self->forkit (
                run     =>  \&announcer,
                body    =>  '/home/somebody/anotherfile.txt',
                channel =>  '#anemptychannel',
              ) unless $self->{log2};
$self->{log2} = 1;

}

sub announcer {
my $announcefile = shift;
my $file=File::Tail->new(name => $announcefile, maxinterval=>5, adjustafter=>7);
while (defined(my $line=$file->read)) { chomp $line; print "$line\n"; }
}

4 个答案:

答案 0 :(得分:1)

我不熟悉你提到的任何模块,但是当我在过去编写过Perl程序时,它通常足以将这一行放在主要的父进程中:

$SIG{CHLD} = sub { wait };

这样,当子进程退出并且父进程获得SIGCHLD信号时,它会自动使用wait重新获得子进程。

答案 1 :(得分:1)

从版本0.82开始,Bot::BasicBot将在退出时终止未完成的子进程(由forkit()创建)。

答案 2 :(得分:1)

我正在挖掘一个旧帖子,但我正在寻找这个问题的答案,Google搜索将此作为最佳结果。所以,如果其他人偶然发现了这一点,我就是这样做的。

在分叉之前,设置pipe以便您的分叉进程可以进行通信:

pipe PARENTRECEIVE,CHILDSEND;

分叉您的流程并让孩子非常地向父母发送新的流程ID。然后,当您认为孩子被挂起时(通过计时器或SIG),您可以杀死子进程:

my $pid = fork();
if ($pid eq 0) {
  #child block
  my $cid = $$;       
  print CHILDSEND "$cid\n";
  <do child stuff which might hang>
}
else {
  #parent block
  my $child_id = <PARENTRECEIVE>;
  chomp $child_id;
  <do parent stuff>
  <if you think child is hung> {
    kill 1, $child_id;
  }
}

有时我们的网络服务器会挂起,所以我用它来检查它是否仍然在合理的时间内响应HTTP GET请求。如果孩子在父母的计时器启动之前没有完成抓取URL,它会发出一封电子邮件提醒,说明事情已经结束。

答案 3 :(得分:0)

我会关注您的子进程未正确终止的原因。在“正常”情况下,父母不应该做任何事情(如果您关心结果,可以致电waitpid,或者在完成之前停止处理)。

这还不是一个很好的答案 - 你可能不得不在孩子们中粘贴一些代码。但是一些建议 - 从手动调用fork的简单程序开始,而不是依靠CPAN模块为您完成。了解系统如何处理多个进程非常重要。然后,当你得到它时,你可以利用一些框架为你处理很多过程。

如果你最近没有这样做,你应该阅读perldoc perlfork,并尝试一些例子。