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

qpgpyjmq  于 2022-11-15  发布在  Perl
关注(0)|答案(5)|浏览(169)

我正在运行一个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.

我还可以做什么来杀死子进程呢?forked进程是使用Bot::BasicBot中的forkit方法创建的。
示例脚本:

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"; }
}
qoefvg9y

qoefvg9y1#

我对您提到的任何模块都不熟悉,但是当我在过去编写forking Perl程序时,通常只需在主父进程中放置下面这行代码:

$SIG{CHLD} = sub { wait };

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

klh5stk1

klh5stk12#

我正在挖掘一个旧的帖子,但我正在寻找这个问题的答案,谷歌搜索把这个作为首要结果。所以,为了防止其他人在这个问题上绊倒,这里是我是如何做到的。
在分叉之前,设置一个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;
  }
}

有时候我们的Web服务器会挂起,所以我用它来检查它是否在合理的时间内仍然响应HTTP GET请求。如果子进程没有在父进程的计时器超时之前完成获取URL,它会发送一封电子邮件警告说发生了什么事情。

ne5o7dgx

ne5o7dgx3#

在“正常”情况下,父进程不应该做任何事情(如果您关心结果,可能调用waitpid,或者暂停处理直到它们完成)。
这还不是一个真正的答案--您可能需要将一些代码粘贴到子进程中。但是有一点建议--从手动调用fork的简单程序开始,而不是依赖CPAN模块来为您完成。了解系统如何处理多个进程是很重要的。当您了解了这些之后,您可以利用一些框架来处理大量流程。
如果您最近还没有通读perldoc perlfork,您可能还应该通读一下,并尝试其中的一些示例。

7vux5j2d

7vux5j2d4#

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

tjvv9vkg

tjvv9vkg5#

Fork将返回父块中子进程的进程ID。
作为对Mintx的答复,不需要管道。

my $pid = fork();
if ($pid == 0) {
  #child block
  <do child stuff which might hang>
}
else {
  #parent block
  <do parent stuff>
  <if you think child is hung> {
    kill 1, $child_id;
  }
}

相关问题