【问题标题】:How do I do a non-blocking IPC read on Windows?如何在 Windows 上进行非阻塞 IPC 读取?
【发布时间】:2010-12-08 11:35:03
【问题描述】:

我有一个 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-&gt;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 上)?

【问题讨论】:

  • 你打算在不阻塞 IO 的情况下做什么?
  • 我想读到我停止获取数据为止,这似乎比通过解析我已经检索到的数据来确定我已经到达终点更可靠。 (数据中没有“这是最后一行”的指示。)
  • 搜索了一个相关的问题,我在 GitHub 上找到了这个提交,它似乎调用了黑魔法来制作一个非阻塞套接字:github.com/kthakore/frozen-bubble/commit/…

标签: windows perl ipc


【解决方案1】:

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

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

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

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

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

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

【讨论】:

  • 是的,Windows 很烂。在那里运行时, IPC::Run 会执行您提到的套接字技巧。
  • 正如我在问题中所写,我无法通过 Fcntl 或 IO::Handle 使句柄非阻塞。如果您知道一种方法(适用于 Windows),请分享。
  • @Michael:在单独的进程中运行阻塞 IO。使用套接字与主进程来回通信。子进程可能会阻塞,但主进程可以在通信套接字上进行非阻塞 I/O。这就是 daotoad 对socketpair 的建议。
  • @ephemient,我必须非常努力地不在我的帖子中包含同样的情绪。
【解决方案2】:

非阻塞 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 缓冲它的输出,这可能不会那么好。

【讨论】:

    【解决方案3】:

    另一个问题是使用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 ...
    

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

    【讨论】:

      猜你喜欢
      • 2016-04-02
      • 1970-01-01
      • 2016-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多