perl - 为什么IPC::Open3陷入僵局?

标签 perl ipc

我浏览了open3的文档,这是我无法理解的部分:

If you try to read from the child's stdout writer and their stderr writer, you'll have problems with blocking, which means you'll want to use select() or the IO::Select, which means you'd best use sysread() instead of readline() for normal stuff.

This is very dangerous, as you may block forever. It assumes it's going to talk to something like bc, both writing to it and reading from it. This is presumably safe because you "know" that commands like bc will read a line at a time and output a line at a time. Programs like sort that read their entire input stream first, however, are quite apt to cause deadlock.


因此,我尝试了open3希望对它有所了解。这是第一次尝试:
sub hung_execute {
    my($cmd) = @_;
    print "[COMMAND]: $cmd\n";
    my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
    print "[PID]: $pid\n";
    waitpid($pid, 0);
    if(<$err>) {
        print "[ERROR] : $_" while(<$err>);
        die;
    }
    print "[OUTPUT]: $_" while (<$out>);
}
有趣的是,我必须在这里初始化$err
无论如何,由于我execute("sort $some_file");是一个包含超过4096个字符(我的机器的限制)的文本文件,因此当我$some_file时,这只是挂起。
然后,我研究了this FAQ,以下是我的execute新版本:
sub good_execute {
    my($cmd) = @_;
    print "[COMMAND]: $cmd\n";
    my $in = gensym();
    #---------------------------------------------------
    # using $in, $out doesn't work. it expects a glob?
    local *OUT = IO::File->new_tmpfile;
    local *ERR = IO::File->new_tmpfile;
    my $pid = open3($in, ">&OUT", ">&ERR", $cmd);
    print "[PID]: $pid\n";
    waitpid($pid, 0);
    seek $_, 0, 0 for \*OUT, \*ERR;
    if(<ERR>) {
        print "[ERROR] : $_" while(<ERR>);
        die;
    }
    print "[OUTPUT]: $_" while (<OUT>);
}
sort命令现在可以正常执行,但是我不知道为什么。
[更新] 在阅读@tchrist的答案后,我阅读了IO::Select,并且在进行了更多的谷歌搜索之后,提出了这个版本的execute:
sub good_execute {
    my($cmd) = @_;
    print "[COMMAND]: $cmd\n";
    my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
    print "[PID]: $pid\n";
    my $sel = new IO::Select;
    $sel->add($out, $err);
    while(my @fhs = $sel->can_read) {
        foreach my $fh (@fhs) {
            my $line = <$fh>;
            unless(defined $line) {
                $sel->remove($fh);
                next;
            }
            if($fh == $out) {
                print "[OUTPUT]: $line";
            }elsif($fh == $err) {
                print "[ERROR] : $line";
            }else{
                die "[ERROR]: This should never execute!";
            }
        }
    }
    waitpid($pid, 0);
}
一切正常,现在有些事情变得更加清楚了。但是总体情况仍然有些模糊。
所以我的问题是:
  • hung_execute怎么了?
  • 我猜good_execute可以工作,因为open3调用中的>&。但是为什么以及如何?
  • 另外,当我为文件句柄使用词法变量(good_execute而不是my $out)时,OUT不起作用。它给出了此错误:open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid argument。为什么这样?
  • 在给定的时间似乎只有一个文件句柄可以写入,并且如果我丢弃了保持资源的句柄,则其他句柄将继续等待。我曾经认为STDERR和STDOUT是独立的流,并且不共享任何资源。我想我的理解在这里有些瑕疵。也请给我一些指示。
  • 最佳答案

    您遇到了我在文档中写到的非常严重的问题,然后遇到了一些问题。您之所以陷入僵局,是因为您在等待 child 退出之前无法朗读。如果它的输出不止管道缓冲区,它将阻塞并退出下一个导出。另外,您还没有闭合 handle 的末端。

    您也有其他错误。您不能通过这种方式测试句柄上的输出,因为您只是执行了阻塞的读取行并丢弃了其结果。此外,如果您尝试读取stdout之前的所有stderr,并且stdout上有多个管道缓冲区输出,则您的 child 将阻止对stdout的写入,同时您阻止对其stderr的读取。

    您确实必须使用selectIO::Select才能正确执行此操作。只有在该句柄上有可用输出时,您才必须从该句柄中进行读取,并且也不得将缓冲的调用与select混合使用,除非您非常幸运。

    关于perl - 为什么IPC::Open3陷入僵局?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10029406/

    相关文章:

    Perl 需要明确的包名

    regex - Perl预编译正则表达式问题

    javascript - 如何在 perl CGI 中获取访问者浏览器窗口大小

    regex - 查找重复字符的单词

    windows - perl模块安装make和nmake比较

    linux - UNIX/Linux IPC : Reading from a pipe. 如何在运行时知道数据的长度?

    c - 从 parent 向 child 发送信号,反之亦然

    c++ - C++ 和 Mathematica 之间的 IPC?

    linux - 几个相同进程之间的IPC通信

    rust shm_open() 函数失败