我的目标是启动一个正在进行的进程(例如 iostat)并解析一些信息。完成后,我想杀死 iostat 并优雅地关闭管道。请记住,iostat 将永远运行,直到我杀死它。
如果我尝试在关闭管道之前终止进程,close() 将返回 -1,表示“无子进程”。如果我在关闭管道之前没有终止进程,它会返回 13,因为 iostat 仍在尝试写入我的管道。换句话说,这个脚本总是会die()。
如何优雅地关闭这个管道?
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;
最佳答案
要理解为什么会发生这种情况,您需要了解关闭此类管道时 Perl 幕后发生的情况。
另请参阅 close 的描述在 perldoc 上它说
If the filehandle came from a piped open, close returns false if one of the other syscalls involved fails or if its program exits with non-zero status.
实际关闭底层文件描述符后,perl 调用系统调用“waitpid()”来收集进程(如果它不这样做,您手上就会有一个“僵尸”进程)。如果进程已经退出,则 waitpid 返回错误代码 ECHILD,“无子进程”。这是 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;
关于Perl - 终止从管道启动的正在进行的进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20084898/