7

我正在尝试编写一个为每个连接分叉的套接字服务器。除了一个小警告外,我已经成功了:我的子进程使用 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 需要正常工作的一些自定义处理程序?

4

2 回答 2

11

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

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

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

于 2010-08-12T04:15:59.447 回答
8

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


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

perlfunc 手册页waitpid部分

如果你说

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

然后您可以对所有待处理的僵尸进程进行非阻塞等待。非阻塞等待在支持 waitpid(2) 或 wait4(2) 系统调用的机器上可用。

于 2010-08-12T04:13:04.317 回答