Получать детей, не устанавливая $ SIG {CHLD} в IGNORE или в собственный обработчик сигналов - PullRequest
7 голосов
/ 12 августа 2010

Я пытаюсь написать сокет-сервер, который разветвляется для каждого соединения. Я добился успеха, за исключением одного небольшого предостережения: мои дочерние процессы используют 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?

Ответы [ 2 ]

11 голосов
/ 12 августа 2010

Продолжите и установите обработчик SIGCHLD в родительском процессе, но отключите его в дочерних процессах - например, введите local $SIG{CHLD} сразу после вызова fork.

В дочерних процессах события SIGCHLD происходят из методов Net::OpenSSH, и модуль Net::OpenSSH будет обрабатывать эти события.

В родительском процессе события SIGCHLD происходят от выхода из вашего дочернего процесса. Это именно те события, которые вас интересуют, и те, с которыми вам нужно справиться, чтобы предотвратить зомби.

8 голосов
/ 12 августа 2010

Если вам никогда не нужно игнорировать дочерние элементы и использовать Net :: OpenSSH в одном и том же процессе, вы можете использовать $SIG{CHLD} = 'IGNORE' в процессах, которые не используют Net :: OpenSSH (например, процесс с одним сервером, который отключается «Автоматически пожинает» дочерние элементы) и сбрасывает его на $SIG{CHLD} = 'DEFAULT' в процессах, использующих Net :: OpenSSH (например, дочерние элементы сервера).


В качестве альтернативы вы можете использовать неблокирующую waitpid в цикле после каждого нового клиентского соединения. Вы все равно можете закончить с одним или несколькими зомби, но они все будут пожинать при следующем соединении. Если вы переключитесь на использование select (или что-то вроде IO :: Select ), вы можете установить верхнюю границу «времени жизни» зомби, выполнив выбор на прослушивание сокета и выполнение раунда неблокирующего зомби, пожигающего после каждого возврата по таймауту, а также каждого возврата «готов к сокету».

Из раздела waitpid из man-страницы perlfunc :

Если вы скажете

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

тогда вы можете сделать неблокирующее ожидание для всех ожидающих процессов зомби. Неблокирующее ожидание доступно на машинах, поддерживающих системные вызовы waitpid (2) или wait4 (2).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...