【发布时间】:2018-02-01 00:24:21
【问题描述】:
作为process hangs when writing large data to pipe 的后续行动,我需要实现一种方法,让父进程从其子进程写入的管道中读取数据,同时执行其他操作直到子进程完成。
更具体地说,父级通过 HTTP 向客户端返回响应。响应由字符串<PING/> 组成,然后是完成ping 时的字符串<DONE/>,然后是实际内容。这样做是为了在实际响应准备好之前保持连接处于活动状态。
我的问题:
1) 我主要是在寻找一般反馈。您是否看到此代码有任何问题?
2) 我能否实现非阻塞读取的目标?特别是一旦读取了所有当前可用的数据(但作者仍在编写更多数据),我的代码是否能够从while ( my $line = <$pipe_reader>){ 继续前进?并且在管道关闭后子节点终止之前它会正常工作吗?
3) IO::Select 的文档说 add() 需要一个 IO::Handle 对象。我一直在到处看到IO::Handle,但我不知道如何确定以这种方式创建的管道是否算作IO::Handle 对象。 perl -e "pipe(my $r, my $w); print(ref($r))" 只是给了我 GLOB...
4) select 的 Perl 文档(我假设 IO::Select 是基于该文档)警告
警告:除非 POSIX 允许,否则不应尝试将缓冲 I/O(如
read或readline)与select混合使用,即使这样也只能在 POSIX 系统上使用。您必须改用sysread。
这是否意味着$writer->write('<PING/>'); 在同一个循环中存在问题?
Perl 代码
pipe(my $pipe_reader, my $pipe_writer);
$pipe_writer->autoflush(1);
my $pid = fork;
if ( $pid ) {
# parent
close $pipe_writer;
$s = IO::Select->new();
$s->add($pipe_reader);
my $response = "";
my $startTime = time;
my $interval = 25;
my $pings = 0;
while ( waitpid(-1, WNOHANG) <= 0 ) {
if ( time > $startTime + ($interval * $pings) ) {
$pings++;
$writer->write('<PING/>');
}
if ( $s->can_read(0) ) {
while ( my $line = <$pipe_reader> ) {
$response .= $line;
}
}
};
$writer->write('<DONE/>');
$writer->write($response);
close $pipe_reader;
$writer->close();
else {
#child
die "cannot fork: $!" unless defined $pid;
close $pipe_reader;
#...do writes here...
close $pipe_writer;
}
关于$writer,可能与本题无关,但整体解决方案遵循
second code sample here
由于我们还没有准备好整个 HTTP 正文,我们向 PSGI 返回一个回调,它为我们提供了一个 $responder 对象。我们只给它 HTTP 状态和内容类型,然后它给我们一个 $writer 以便稍后编写正文。
我们在上面的代码中使用$writer 来编写我们的 ping 值和最终的正文。以上所有代码都在返回到 PSGI 的回调中,但为简洁起见,我省略了。
【问题讨论】:
-
你结婚了吗?文件的容量是无限的(不超过你的磁盘空间),而套接字的容量通常比管道大。