2

我的理解是关闭IO::Pipe对象的句柄应该使用方法($fh->close)而不是内置的(close($fh))。

前几天,我出于习惯在一个对象上犯了错误并使用了内置,该IO::Pipe对象打开了一个我预计会失败的命令。零时我很惊讶$?,而且我的错误检查没有被触发。

我意识到我的错误。如果我使用内置,IO:Pipe则无法执行waitpid()并且无法设置$?。但令我惊讶的是 perl 似乎仍然关闭管道而不$?通过核心设置。

我编写了一个小测试脚本来说明我的意思:

use 5.012;
use warnings;

use IO::Pipe;

say 'init pipes:';
pipes();
my $fh = IO::Pipe->reader(q(false));
say 'post open pipes:';
pipes();

say 'return: ' . $fh->close;
#say 'return: ' . close($fh);
say 'status: ' . $?;
say q();

say 'post close pipes:';
pipes();

sub pipes
   {
   for my $fd ( glob("/proc/self/fd/*") )
      {
      say readlink($fd) if -p $fd;
      }
   say q();
   }

使用该方法时,它显示管道在关闭后消失$?并按我的预期设置:

init pipes:

post open pipes:
pipe:[992006]

return: 1
status: 256

post close pipes:

而且,当使用内置时,它似乎也关闭了管道,但没有设置$?

init pipes:

post open pipes:
pipe:[952618]

return: 1
status: 0

post close pipes:

对我来说,内置导致管道关闭,但没有设置,这似乎很奇怪$?。任何人都可以帮助解释差异吗?

谢谢!

4

3 回答 3

2

如果您查看IO::Handle(其中IO::Pipe::End是一个子类)的代码,您将看到以下内容:

sub close {
    @_ == 1 or croak 'usage: $io->close()';
    my($io) = @_;

    close($io);
}

看起来$fh->close只是调用close $fh。当然,我们不应该在幕后偷看。

我们可以看到之后IO::Pipe执行了一个close $fh(在幕后),然后执行了一个 waitpid:

package IO::Pipe::End;

our(@ISA);

@ISA = qw(IO::Handle);

sub close {
    my $fh = shift;
    my $r = $fh->SUPER::close(@_);   # <-- This just calls a CORE::close

    waitpid(${*$fh}{'io_pipe_pid'},0)
        if(defined ${*$fh}{'io_pipe_pid'});

    $r;
}

同样有趣的是来自关闭的 Perldoc:

如果文件句柄来自管道打开,则如果涉及的其他系统调用之一失败或者如果其程序以非零状态退出,则关闭返回 false。如果唯一的问题是程序以非零值退出,$! 将设置为 0 。

关闭管道还会等待管道上执行的进程退出——以防您希望查看管道的输出

之后——并隐式将该命令的退出状态值放入 $? 和 ${^CHILD_ERROR_NATIVE} 。

这回答了你的问题。

于 2013-10-29T22:20:30.870 回答
1

但令我惊讶的是 perl 似乎仍然在不设置 $ 的情况下关闭管道?通过核心。

为什么会呢?它无法知道另一端的进程是一个子进程,更不用说程序应该等待的进程了。由于它没有理由调用waitpid,$?不会被设置。

事实上,我怀疑它是否愿意等待管道另一端的进程,因为我怀疑是否有办法在管道的另一端获取进程的 pid,因为它实际上是可能的成为管道另一端的多个进程。

IO::Pipe::closewaitpid仅在 IO::Pipe 用于“打开进程”时调用。

同样,close仅调用waitpidwhenopen用于“打开一个进程”。

使用一种方法“打开”的进程不能被另一种方法关闭。

于 2013-10-30T02:52:41.837 回答
0

事实证明,我的困惑源于一个有缺陷的假设,即消失的管道与完整的进程终止同时发生。情况似乎并非如此,因为该过程仍可用于wait().

> perl -MIO::Pipe -le 'my $io = IO::Pipe->reader(q(false)); close($io); print $?; print wait(); print $?'
0
8857
256
于 2013-10-30T03:52:41.087 回答