如果大量输出被写入 stdout 和/或 stderr,或者您正在读取和写入进程。您需要更加小心地处理 I/O 以避免各种阻塞问题。
my ($wtr, $rdr, $err) ;
my $pid = IPC::Open3::open3($wtr, $rdr, $err, @_);
close($wtr);
my $stdout = '';
my $stderr = '';
my $s = IO::Select->new;
$s->add($rdr) if $rdr;
$s->add($err) if $err;
while (my @ready = $s->can_read) {
foreach my $ioh (@ready) {
my $bytes_read = sysread($ioh, my $chunk = '', 1024);
die "read error: $!" unless $bytes_read >= 0;
if ($bytes_read) {
($ioh eq $rdr? $stdout: $stderr) .= $chunk;
}
else {
$s->remove($ioh);
}
}
}
my $pid1;
for (;;) {
last if kill(0, $pid);
$pid1 = wait();
#
# Wait until we see the process or -1 (no active processes);
#
last if ($pid1 == $pid || $pid1 <= 0);
}
在关闭进程之前完成阅读。如果您正在写入进程的标准输入,您还需要将 $wtr 和 syswrite 添加到上述选择循环中。
编辑
理由:
对于简单的情况,以上内容可能是矫枉过正。当您可能移动超过几 K 的数据时,这种输入和输出的高级处理就会发挥作用。
例如,如果您正在执行“df”命令,则不需要它。
然而,当 stdin、stdout 或 stderr 中的任何一个的系统缓冲区填满时,阻塞变得可能,事情可能会变得更加复杂。
如果子进程填满了 stderr 和/或 stdout 缓冲区,它可能会阻塞并等待您清除它们。但是,如果您在从 stdout 或 stderr 读取之前等待进程完成;那是一个僵局。您可能会看到系统调用永远不会完成,子进程永远不会完成。
如果正在写入标准输入,也有类似的死锁可能性,但子进程无法使用输入。在子进程消耗输入并写入标准输出的“管道”情况下,这种情况尤其可能发生。
选择循环是关于逐步清除缓冲区以避免阻塞。stdout 和 stderr 同时受到监控。
如果您正在写入标准输入并从标准输出(管道)读取,您需要保持标准输出和标准错误清晰,并且仅在标准输入准备好接收输入时写入。
只需等待该过程完成,然后读取 stdout/stderr 可能在 90% 的时间内都有效。如果事情变得更加复杂并且进程开始阻塞或陷入僵局,这个回复只是给你一个去处。
编辑2
至于使用哪个,我想说从简单开始,努力测试。
采用 Sorpigal 的方法,但尝试在更高的数据量和更困难的负载和条件下进行压力测试,这是您在实时系统中所期望的。