Perl - 杀死从管道发起的持续进程

时间:2013-11-20 00:10:12

标签: perl pipe kill iostat

我的目标是启动一个持续的过程(例如iostat)并解析一些信息。一旦我完成,我想杀死iostat并优雅地关闭管道。请记住,iostat会一直运行直到我杀了它。

如果我在关闭管道之前尝试终止进程,则close()会为“no children”返回-1。如果我在关闭管道之前没有终止进程,它会返回13,因为iostat仍在尝试写入我的管道。换句话说,这个脚本总是会死()。

如何正常关闭此管道?

use warnings;
use strict;

my $cmd = "iostat 1";
my $pid = open(my $pipe, "$cmd |") || die "ERROR: Cannot open pipe to iostat process: $!\n";
my $count = 0;

while (<$pipe>){
  if ($count > 2){ 
    kill(9, $pid);    # if I execute these two lines, close() returns -1
    waitpid($pid, 0); # otherwise it returns 13
    last;
  }
  $count++;
}
close($pipe) || die "ERROR: Cannot close pipe to iostat process: $! $?\n";
exit 0;

2 个答案:

答案 0 :(得分:3)

要了解为什么会发生这种情况,您需要了解关闭此类管道后幕后的perl内容。

另请参阅perldoc上的close说明

  

如果文件句柄来自管道打开,如果涉及的其他系统调用之一失败或其程序以非零状态退出,则close返回false。

在实际关闭底层文件描述符之后,perl调用系统调用'waitpid()'来收集进程(如果它不这样做,你手上就会有'僵尸'进程)。如果进程已经退出,则waitpid将返回错误代码ECHILD,“No child processes”。这是perl从close函数报告的错误代码。

您可以通过从循环内部删除“waitpid”调用来避免这种情况,以便perl可以在关闭管道时执行此操作。但是你仍然会得到一个错误,因为来自被杀死进程的返回码包含终止进程的信号编号或者实际返回码向左移8位。您可以通过检查$来处理它! == 0和$? == 9(或您使用的任何信号编号)。

所以你的代码可能如下所示:

use warnings;
use strict;

my $cmd = "iostat 1";
my $pid = open(my $pipe, "$cmd |") || die "ERROR: Cannot open pipe to iostat process: $!\n";
my $count = 0;

while (<$pipe>){
  if ($count > 2){
    kill(9, $pid);    # if I execute these two lines, close() returns -1
    last;
  }
  $count++;
}
unless (close($pipe)) {
        die "ERROR: Cannot close pipe to iostat process: $! $?\n" if $! != 0 || $? != 9;
}
exit 0;

答案 1 :(得分:1)

我会依赖perl来完成程序完成时所需的所有内务处理。 close仅在您可能用完文件句柄时,或者如果您想确保输出句柄上没有数据丢失时才是关键。

我也会使用open中的三参数,因为建议这样做。

喜欢这个

use strict;
use warnings;

my $cmd   = 'iostat 1';
my $pid   = open my $pipe, '-|', $cmd or die "ERROR: Cannot open pipe from command: $!";
my $count = 0;

while (<$pipe>){
  last if $count > 2;
  $count++;
}