【问题标题】:How do you tell if a pipe opened process has terminated?你如何判断一个管道打开的进程是否已经终止?
【发布时间】:2011-09-16 17:11:45
【问题描述】:

假设使用以下代码创建句柄:

use IO::File;

my $fh = IO::File->new;

my $pid = $fh->open('some_long_running_proc |') or die $!;

$fh->autoflush(1);
$fh->blocking(0);

然后用这样的循环读取:

while (some_condition_here) {
    my @lines = $fh->getlines;
    ...
    sleep 1;
}

如果管道另一端的进程已终止,我将返回 false 的 some_condition_here 是什么?

测试$fh->eof 将不起作用,因为该进程仍可以在不打印任何新行的情况下运行。测试$fh->opened 似乎没有任何用处。

目前我正在使用$pid =! waitpid($pid, WNOHANG),它似乎在符合 POSIX 的环境中工作。这是最好的方法吗?在 Windows 上呢?

【问题讨论】:

  • $fh->blocking(0) 甚至可以在 Windows 上运行吗?我认为有 some heavy wizardry 参与让非阻塞 I/O 工作。

标签: perl file-io pipe ipc


【解决方案1】:

等待实际的 EOF 有什么问题?

while (<$fh>) {
    ...
    sleep 1;
}

您已经为非阻塞读取设置了句柄,所以它应该做正确的事情。确实,以您的示例为例,您甚至不需要设置非阻塞并且可以摆脱睡眠。

您在等待some_long_running_proc 时还有其他事情要做吗?如果是这样,select 可能就在你的未来。

【讨论】:

  • 我给出的示例过于简化,打开了多个文件句柄,while 循环会轮询所有文件句柄,所以我不能让它阻塞在任何单个句柄上。
  • @Eric Strom, select 会告诉您哪些句柄需要维修,包括那些已关闭的句柄。跟进 sysread 以检查 EOF。我建议使用 [IO::Select] 而不是直接使用select
【解决方案2】:

您使用 select() 来确定是否有任何数据,或关闭等异常情况。

就我个人而言,我更喜欢使用 IO::Multiplex,尤其是在您从多个不同的描述符多路复用输入的情况下,但这可能不适用于这种情况。

【讨论】:

    【解决方案3】:

    有很多选择。

    • readline aka &lt;$fh&gt; 将在 eof(或错误)时返回 false。

    • eof 将在 eof 时返回 true。

    • read(块大小 > 0)将在 eof 时返回已定义且为零。

    • sysread(块大小 > 0)将在 eof 时返回已定义和零。

    您可以使用select 或在上述任何操作之前使句柄非阻塞以进行无阻塞检查。

    【讨论】:

    • eof 只是告诉您当前没有更多输入要读取。它不会告诉你管道的另一端是否会在以后给你更多的输入。
    • @mob,你错了。如果句柄的缓冲区不为空,eof 返回 true。如果句柄的缓冲区为空,则eof 执行read 并在read 返回true 时返回true。
    • @mob,您应该在反驳某人之前验证您的声明。
    • @mob, perl -MData::Dumper -e'open(my $fh, "cat|") or die; print(Dumper(eof($fh)));'
    • 我坚持我的主张,即eof 可以为真,而管道的另一端仍处于活动状态,以后可能变为假。例如use IO::Handle;pipe $R,$W;$W-&gt;autoflush(1); $R-&gt;blocking(0);if(!fork()){for(1..5){sleep$_;print $W "$_\n"}exit}print"eof=",$R-&gt;eof;sleep 2;print"\neof=",$R-&gt;eof
    【解决方案4】:

    关于使用select

    use strict;
    use warnings;
    
    use IO::Select qw( );
    
    sub process_msg {
        my ($client, $msg) = @_;
        chomp $msg;
        print "$client->{id} said '$msg'\n";
        return 1;  # Return false to close the handle.
    }
    
    my $select = IO::Select->new();
    my %clients;
    
    for (...) {
        my $fh = ...;
        $clients{fileno($fh)} = {
            id  => '...'
            buf => '',
            # ...
        };
    
        $select->add($fh);
    }
    
    while (my @ready = $select->can_read) {
        for my $fh (@ready) {
            my $client = $clients{ fileno($fh) };
            our $buf; local *buf = \( $client->{buf} );
    
            my $rv = sysread($fh, $buf, 64*1024, length($buf));
            if (!$rv) {
                if (defined($rv)) {
                    print "[$client->{id} ended]\n";
                } else {
                    print "[Error reading from $client->{id}: $!]\n";
                }
    
                print "[Incomplete message received from $client->{id}]\n"
                    if length($buf);
    
                delete $clients{ fileno($fh) };
                $select->remove($fh);
                next;
            }
    
            while ($buf =~ s/^(.*\n)//) {
                if (!process_msg($client, "$1")) {
                    print "[Dropping $client->{id}]\n";
                    delete $clients{ fileno($fh) };
                    $select->remove($fh);
                    last;
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 2011-07-27
      • 1970-01-01
      • 1970-01-01
      • 2011-06-05
      • 2019-01-27
      • 1970-01-01
      相关资源
      最近更新 更多