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

如何在Windows上执行非阻塞IPC读取?,windows,perl,ipc,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>; 我已尝试通过fcntl为非阻塞

我有一个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>;
我已尝试通过
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上)使其工作吗?

在Windows中。它看起来像IPC::OpenX使用普通的文件句柄,因此您将无法使用它创建的句柄来使用
select

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

如果你需要更细致的控制,这可能对你很有用

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

如果您尝试为一个没有控制台运行的程序克隆
STDOUT
STDERR
(即它是用wperl而不是perl启动的),您将无法通过STDIO获取数据


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

非阻塞IO在Windows上很难实现。在这种情况下,您可以将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 ...
my($cin,$cout);
my$somefile=“some/file”;
打开($cin,“| cleartool>$somefile”);

open($cout,另一个难题是使用缓冲区大小较大或不大可能的
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 ...
print$cin“$command\u for\u cleartool\n”;
我的($description,$maxlen,$buffer)=(“”,65336);
while(my$n=sysread$cout、$buffer、$maxlen){
$description.=$buffer;
如果$n<$maxlen,则为最后一个;
}
…使用$description执行某些操作。。。

sysread
如果正好有0个字节的输入等待读取,则将挂起。因此,如果cleartool恰好生成65336字节的倍数,则上面的代码将挂起。如果您知道程序输出大小的良好上限,则可以将该值用于上面的
$maxlen
。否则,您可以选择一个大的未挂起的值ely number and pray…

是的,Windows很糟糕。在那里运行时,IPC::Run执行您提到的套接字技巧。正如我在问题中所写的,我无法通过Fcntl或IO::handle使句柄非阻塞。如果您知道一种方法(在Windows上工作)请分享。@Michael:在一个单独的进程中运行阻塞IO。使用套接字与主进程来回通信。子进程可能会阻塞,但主进程可以在通信套接字上执行非阻塞I/O。这是daotoad对
socketpair
@ephemient的建议,我不得不非常努力地避免包含我的帖子中也有同样的观点。在不阻塞IO的情况下,你打算做什么?我想一直读到停止获取数据,这似乎比试图通过解析我已经检索到的数据来确定我已经到达终点更可靠。(数据中没有“这是最后一行”的指示。)在四处搜索相关问题时,我在GitHub上发现了这个提交,它似乎调用了黑魔法来创建一个非阻塞套接字:
    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 ...