无需将 $SIG{CHLD} 设置为 IGNORE 或自定义信号处理程序即可获取子级
我正在尝试编写一个为每个连接分叉的套接字服务器。除了一个小警告之外,我已经成功了:我的子进程使用 Net:OpenSSH->capture2() ,这要求 $SIG{CHLD} 不得设置为 IGNORE 或自定义信号处理程序。如何在不设置信号处理程序或使用 wait 或 waitpid 减慢父进程的情况下收割我的子进程?
这是我的服务器代码:
my $sock = new IO::Socket::INET (
LocalHost => 'localhost',
LocalPort => '1337',
Proto => 'tcp',
Listen => SOMAXCONN,
Reuse => 1,
);
die "Could not create socket: $!\n" unless $sock;
my $new_client, $pid;
while($new_client = $sock->accept()){
next if $pid = fork;
die "fork: $!" unless defined $pid;
close $sock;
while(<$new_client> ) {
#do Net::OpenSSH stuff
}
exit;
} continue {
close $new_client;
}
如果我使用上面所示的代码,一切正常,但最终会出现一堆僵尸进程。如果我添加
local $SIG{CHLD} = 'IGNORE';
僵尸被收割,但 Net::OpenSSH->capture2() 方法调用的返回代码很混乱。我假设我的信号处理程序正在干扰 Net::OpenSSH 需要正常工作的某些自定义处理程序?
I am trying to write a socket server that forks for every connection. I have been successful except for one small caveat: my child processes use Net:OpenSSH->capture2() which requires that $SIG{CHLD} not be set to IGNORE or to a custom signal handler. How can I reap my children without setting the signal handler or slowing down the parent process with wait or waitpid?
Here is my server code:
my $sock = new IO::Socket::INET (
LocalHost => 'localhost',
LocalPort => '1337',
Proto => 'tcp',
Listen => SOMAXCONN,
Reuse => 1,
);
die "Could not create socket: $!\n" unless $sock;
my $new_client, $pid;
while($new_client = $sock->accept()){
next if $pid = fork;
die "fork: $!" unless defined $pid;
close $sock;
while(<$new_client> ) {
#do Net::OpenSSH stuff
}
exit;
} continue {
close $new_client;
}
If I use the code as shown above, everything works but I end up with a bunch of zombie processes. If I add
local $SIG{CHLD} = 'IGNORE';
the zombies are reaped, but the Net::OpenSSH->capture2() method call has a messed up return code. I'm presuming my signal handler is interfering with some custom handler that Net::OpenSSH needs to work properly?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
继续在父进程中设置 SIGCHLD 处理程序,但在子进程中禁用它 - 例如在
fork
之后立即放置一个local $SIG{CHLD}
称呼。在子进程中,SIGCHLD 事件来自 Net::OpenSSH 方法,并且 Net::OpenSSH 模块将处理这些事件。
在父进程中,SIGCHLD 事件来自子进程的退出。这些正是您感兴趣的事件,也是您需要处理以防止僵尸发生的事件。
Go ahead and set up a SIGCHLD handler in the parent process, but disable it in the child processes -- for example put a
local $SIG{CHLD}
immediately after thefork
call.In the child processes, SIGCHLD events come from the
Net::OpenSSH
methods, and theNet::OpenSSH
module will handle those events.In the parent process, SIGCHLD events come from your child processes exiting. Those are exactly the events you are interested in and the ones you need to handle to prevent zombies.
如果您不需要忽略子进程并在同一进程中使用 Net::OpenSSH,那么您可以在不使用 Net:: 的进程中使用 $SIG{CHLD} = 'IGNORE' OpenSSH(例如,分叉“自动获取”子进程的单个服务器进程)并在使用 Net::OpenSSH 的进程(例如,子进程)中将其重置为
$SIG{CHLD} = 'DEFAULT'
服务器的)。或者,您可以在在每个新的客户端连接后循环。你仍然可能会遇到一个或多个僵尸在周围徘徊,但它们将在下一次连接时全部被收割。如果您切换到使用
select
(或类似 IO::Select),您可以通过以下方式设置僵尸的“生命周期”上限在监听套接字上进行选择,并在每次超时返回以及每次“套接字就绪”返回后进行一轮非阻塞僵尸收割。来自 perlfunc 手册页:
If you never need to ignore children and use Net::OpenSSH in the same process then you might be able to use
$SIG{CHLD} = 'IGNORE'
in the processes that do not use Net::OpenSSH (e.g. the single server process that forks off “auto-reaped” children) and reset it to$SIG{CHLD} = 'DEFAULT'
in the processes that use Net::OpenSSH (e.g. the children of the server).Alternatively, you could use a non-blocking
waitpid
in a loop at after each new client connection. You can still end up with one or more zombies hanging around, but they will all be reaped at the next connection. If you switch to usingselect
(or something like IO::Select), you could set an upper bound on the “lifetime” of the zombies by doing a select on your listening socket and doing a round of non-blocking zombie reaping after every timeout return as well as every “socket ready” return.From the
waitpid
section of perlfunc manpage: