如何在 Windows 上进行非阻塞 IPC 读取?
我有一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
select
仅适用于 Windows 中的套接字。看起来 IPC::OpenX 使用普通文件句柄,因此您将无法将select
与其创建的句柄一起使用。如果您不需要 select 提供的超时/活动检测,您可以将句柄设置为非阻塞,只需按正常方式读取或写入。
如果您需要更细致的控制,IPC::Run 可能很适合您。
您还可以考虑创建一个
socketpair
并将这些句柄用于您的子进程。较新的 perls(5.8 及更高版本)支持使用 TCP 套接字在 Windows 上进行socketpair
模拟。如果您尝试克隆
STDOUT
和STDERR
来运行没有控制台的程序(即它是使用 wperl 而不是 perl 启动的),您将无法通过STDIO获取数据。实际上,这对我的几个项目来说都是一个巨大的痛苦。我发现最有效的方法是编写子进程通过 TCP 连接到父服务器。如果您不控制子进程,请查看
IPC::Run
或socketpair
。select
only works on sockets in Windows. It looks like IPC::OpenX uses normal filehandles, so you won't be able to useselect
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) supportsocketpair
emulation on Windows using TCP sockets.If you try to clone
STDOUT
andSTDERR
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
orsocketpair
.另一个混乱是使用具有较大或不太可能的缓冲区大小的
sysread
。如果正好有 0 个字节的输入等待读取,
sysread
将挂起。因此,如果cleartool 恰好生成 65336 字节的某个倍数,则上面的代码将挂起。如果您清楚地知道程序输出大小的上限,则可以使用上面的$maxlen
的值。否则,你可以选择一个大且不太可能的数字并祈祷......Another kludge is to use
sysread
with a large or unlikely buffer size.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 ...非阻塞 IO 在 Windows 上很难。在这种情况下,您可以将cleartool的输出发送到常规文件,并使用
seek
在每次从文件读取时重置文件上的eof标志:尽管这可能不起作用如果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:Though this will probably not work that well if cleartool buffers its output.