如何在Windows上进行非阻塞IPC读取?

时间:2009-10-20 19:26:57

标签: windows perl ipc

我有一个Perl脚本,它使用外部工具(cleartool)来收集有关文件列表的信息。我想使用IPC来避免为每个文件生成一个新进程:

use IPC::Open2;
my ($cin, $cout);
my $child = open2($cout, $cin, 'cleartool');

返回单行的命令运行良好。 e.g。

print $cin "describe -short $file\n";
my $description = <$cout>;

返回多行的命令让我处于死路,以便如何使用整个响应而不会被阻塞读取挂起:

print $cin "lshistory $file\n";
# read and process $cout...

我尝试通过fcntl设置非阻塞读取的文件句柄:

use Fcntl;
my $flags = '';
fcntl($cout, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl($cout, F_SETFL, $flags);

但是Fcntl的消息是“你的供应商还没有定义Fcntl宏F_GETFL。”

我尝试使用IO :: Handle设置$cout->blocking(0)但是失败了(它返回undef并将$!设置为“未知错误”。)

在尝试阅读之前,我尝试使用select来确定是否有可用数据:

my $rfd = '';
vec($rfd, fileno($cout), 1) = 1;
while (select($rfd, undef, undef, 0) >= 0) {
    my $n = read($cout, $buffer, 1024);
    print "Read $n bytes\n";
    # do something with $buffer...
}

但是没有读过任何东西就挂了。有谁知道如何使这项工作(在Windows上)?

3 个答案:

答案 0 :(得分:5)

Windows中的

select only works on sockets。看起来IPC :: OpenX使用普通的文件句柄,因此您无法将select与其创建的句柄一起使用。

如果您不需要select提供的活动的超时/检测,您可以将句柄设置为非阻塞,并按正常方式读取或写入。

如果您需要更细致的控制,IPC::Run可能适合您。

您还可以查看创建socketpair并将这些句柄与子进程一起使用。较新的perls(5.8及更高版本)支持使用TCP套接字在Windows上进行socketpair仿真。

如果您尝试克隆STDOUTSTDERR以获取在没有控制台的情况下运行的程序(即使用wperl而不是perl启动),您将无法获取数据STDIO。

在实践中,这对我来说在几个项目上都是一个巨大的痛苦。我发现最有效的方法是编写子进程以通过TCP连接到父服务器。如果您不控制子流程,请查看IPC::Runsocketpair

答案 1 :(得分:1)

另一个问题是使用sysread具有较大或不太可能的缓冲区大小。

    print $cin "$command_for_cleartool\n";
    my ($description, $maxlen, $buffer) = ("", 65336);
    while (my $n = sysread $cout, $buffer, $maxlen) {
        $description .= $buffer;
        last if $n < $maxlen;
    }
    ... do something with $description ...
如果正好有0字节的输入等待读取,

sysread将挂起。因此,如果cleartool产生65336字节的正好倍数,则上面的代码将挂起。如果您知道程序输出大小的上限,则可以将该值用于上面的$maxlen。否则,你可以选择一个大而且不太可能的数字并祈祷......

答案 2 :(得分:0)

Windows上的非阻塞IO很难。在这种情况下,您可以将cleartool的输出发送到常规文件,并在每次从文件中读取时使用seek重置文件上的eof标志:

my($cin, $cout);
my $somefile = "some/file";
open($cin, "| cleartool > $somefile");
open($cout, '<', $somefile);

...

print $cin "$command_for_cleartool\n";
# if necessary, wait until cleartool finishes with new output
seek $cout, 0, 1;     # clear eof condition from previous read
my @cleartool_output = <$cout>;  # capture recent output
... process output ...

如果cleartool缓冲其输出,这可能不会那么好。