无需将$ SIG {CHLD}设置为IGNORE或自定义信号处理程序即可收获子项

时间:2010-08-12 03:23:28

标签: perl parent-child signal-handling

我正在尝试编写一个为每个连接分配的套接字服务器。除了一个小警告之外我一直很成功:我的子进程使用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-&gt; capture2()方法调用有一个混乱的返回代码。我假设我的信号处理程序干扰了Net :: OpenSSH需要正常工作的一些自定义处理程序?

2 个答案:

答案 0 :(得分:11)

继续在父进程中设置SIGCHLD处理程序,但在子进程中禁用它 - 例如在local $SIG{CHLD}调用之后立即放置fork

在子流程中,SIGCHLD事件来自Net::OpenSSH方法,Net::OpenSSH模块将处理这些事件。

在父进程中,SIGCHLD事件来自您的子进程退出。这些正是您感兴趣的事件以及您需要处理以防止僵尸的事件。

答案 1 :(得分:8)

如果您永远不需要忽略子项并在同一进程中使用Net :: OpenSSH,那么您可以在不使用Net :: OpenSSH的进程中使用$SIG{CHLD} = 'IGNORE'(例如单个服务器进程在使用Net :: OpenSSH的进程(例如服务器的子进程)中将“自动获取”的子进程分离并重置为$SIG{CHLD} = 'DEFAULT'


或者,您可以在每个新客户端连接之后的循环中使用非阻塞waitpid。你仍然可以在一个或多个僵尸身边闲逛,但是它们都会在下一次连接时被收获。如果您切换到使用select(或类似IO::Select之类的东西),您可以通过在侦听套接字上执行选择并执行一轮非选择来设置僵尸“生命周期”的上限在每次超时返回后阻止僵尸收割以及每次“套接字就绪”返回。

来自waitpid sectionperlfunc manpage

  

如果你说

use POSIX ":sys_wait_h";
#...
do {
    $kid = waitpid(-1, WNOHANG);
} while $kid > 0;
     

然后你可以对所有挂起的僵尸进程进行非阻塞等待。支持waitpid(2)或wait4(2)系统调用的计算机上可以使用非阻塞等待。