如果孩子手动收割,IO::Socket::accept 只能工作一次
Posted
技术标签:
【中文标题】如果孩子手动收割,IO::Socket::accept 只能工作一次【英文标题】:IO::Socket::accept works only once if children reaped manually 【发布时间】:2015-09-15 21:42:06 【问题描述】:在下面的简单回显服务器中,accept()
在第二次调用时返回undef
,如果我使用自己的SIGCHLD
处理程序而不是让 Perl 来处理子进程:
#!/opt/perl5/bin/perl
use IO::Select;
use IO::Socket;
use POSIX qw(WNOHANG);
use strict;
use warnings;
$|=1;
use constant LISTEN_PORT => 9998;
my $server = IO::Socket::INET->new (
Proto => 'tcp',
LocalPort => LISTEN_PORT,
Listen => SOMAXCONN,
Reuse => 1);
(! $server) && die "Could not setup server - $!\n";
$server->autoflush(1);
sub reaper
while ((my $dead_child = waitpid(-1, WNOHANG)) > 0)
print "Reaped PID $dead_child\n";
$SIGCHLD = \&reaper;
;
$SIGCHLD = \&reaper; ## THIS BLOWS
# $SIGCHLD = 'IGNORE'; ## THIS WORKS
while (my $client = $server->accept())
my $childPid;
if (! defined($childPid = fork()))
die "Could not fork: $!\n";
if ($childPid == 0)
print $client $_ while (<$client>);
$client->shutdown(1);
exit();
else
print "Spawned PID $childPid\n";
close($client);
print "bye\n";
换句话说:
$ ./echoserver.pl
Spawned PID 20953
Reaped PID 20953
bye
【问题讨论】:
您使用IO::Select
等低级机制手动创建所有这些工作的任何特殊原因......而不是使用它的更高级别包装之一?
@LeoNerd,我想要一个在$HOME/lib/perl5
中不使用额外模块的小型便携式脚本;事实上,这就是整个代码。不管怎样,你最喜欢的包装是什么?
如果你想要一些可移植的东西,你可以将所需的模块一起打包成一个文件。这样一来,您仍然可以使用 CPAN 中的任何内容,但要知道它们都会被捆绑到一个文件中。
【参考方案1】:
当您的程序接收到SIGCHLD
信号时,底层$server->accept()
的系统调用可能会被中断。在这种情况下,accept()
将返回 undef
,$!
和 $!EINTR
将被设置。
在这种情况下需要更多的防御性编程。这是一种方法:
sub robust_accept
my $server = shift;
for (;;)
my $client = $server->accept();
return $client if $client;
warn "syserror: $!" if !$!EINTR;
while (my $client = robust_accept($server))
...
【讨论】:
你说得对,我忘记了邪恶,狡猾的EINTR
- 非常感谢! IGNORE
处理程序拉到这里有什么有用的恶作剧?
IGNORE
将在操作系统级别捕获信号,而 perl 不会意识到它。以上是关于如果孩子手动收割,IO::Socket::accept 只能工作一次的主要内容,如果未能解决你的问题,请参考以下文章