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

发布于 2024-08-08 20:14:30 字数 1114 浏览 0 评论 0原文

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

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

返回单行的命令效果很好。例如,

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 上)?

I have a Perl script that uses an external tool (cleartool) to gather information about a list of files. I want to use IPC to avoid spawning a new process for each file:

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

Commands that return single-lines work well. e.g.

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

Commands that return multiple lines have me at a dead end for how to consume the entire response without getting hung up by a blocking read:

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

I've tried to set the filehandle for non-blocking reads via fcntl:

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

but Fcntl dies with the message "Your vendor has not defined Fcntl macro F_GETFL."

I've tried using IO::Handle to set $cout->blocking(0) but that fails (it returns undef and sets $! to "Unknown error").

I've tried to use select to determine if there's data available before attempting to read:

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...
}

but that hangs without ever reading anything. Does anyone know how to make this work (on Windows)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

稚气少女 2024-08-15 20:14:30

select 仅适用于 Windows 中的套接字。看起来 IPC::OpenX 使用普通文件句柄,因此您将无法将 select 与其创建的句柄一起使用。

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

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

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

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

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

select only works on sockets in Windows. It looks like IPC::OpenX uses normal filehandles, so you won't be able to use select with the handles it creates.

If you don't need the timeout/detection of activity that select provides, you can set the handles to be non-blocking and just read or write as per normal.

If you need more nuanced control, IPC::Run may work well for you.

You could also look at creating a socketpair and use those handles with your child processes. Newer perls (5.8 and up) support socketpair emulation on Windows using TCP sockets.

If you try to clone STDOUT and STDERR for a program that runs without a console (ie it is started with wperl, instead of perl), you won't be able to get data through STDIO.

In practice this has been a huge pain for me on several projects. What I found worked best was to write the child process to connect to the parent server via TCP. If you don't control the child processes, look at IPC::Run or socketpair.

无人接听 2024-08-15 20:14:30

另一个混乱是使用具有较大或不太可能的缓冲区大小的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 的值。否则,你可以选择一个大且不太可能的数字并祈祷......

Another kludge is to use sysread with a large or unlikely buffer size.

    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 ...

sysread will hang if there are exactly 0 bytes of input waiting to be read. So the code above will hang if cleartool produces exactly some multiple of 65336 bytes. If you know a good upper bound on the size of output from the program, you can use that value for $maxlen above. Otherwise, you could pick a large and unlikely number and pray ...

残疾 2024-08-15 20:14:30

非阻塞 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 ...

尽管这可能不起作用如果cleartool缓冲它的输出就好了。

Non-blocking IO is hard on Windows. In this case, you could send the output of cleartool to a regular file, and to use seek to reset the eof flag on the file each time out read from the file:

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 ...

Though this will probably not work that well if cleartool buffers its output.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文