2

我的目标是启动一个正在进行的进程(例如 iostat)并解析一些信息。完成后,我想杀死 iostat 并优雅地关闭管道。请记住,iostat 将永远运行,直到我将其杀死。

如果我尝试在关闭管道之前终止该进程,则 close() 将返回 -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;
4

2 回答 2

3

要了解为什么会发生这种情况,您需要了解当您关闭这样的管道时 perl 在幕后发生了什么。

另请参阅perldoc 上的close描述

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

在实际关闭底层文件描述符之后,perl 调用系统调用 'waitpid()' 来获取进程(如果它没有这样做,您将拥有一个 'zombie' 进程)。如果进程已经退出,则 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;
于 2013-11-20T07:01:02.110 回答
1

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

我也会使用 of 中的三参数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++;
}
于 2013-11-20T01:00:13.707 回答