Пожинайте дочерние элементы, не устанавливая для $SIG{CHLD} значение IGNORE или настраиваемый обработчик сигналов.

Я пытаюсь написать сервер сокетов, который разветвляется для каждого соединения. Я добился успеха, за исключением одного небольшого предостережения: мои дочерние процессы используют Net:OpenSSH->capture2(), который требует, чтобы для $SIG{CHLD} не было установлено значение IGNORE или пользовательский обработчик сигналов. Как я могу собрать свои дочерние элементы, не устанавливая обработчик сигналов или замедляя родительский процесс с помощью ожидания или ожидания?

Вот мой код сервера:

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 должен работать правильно?


person Michael    schedule 12.08.2010    source источник


Ответы (2)


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

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

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

person mob    schedule 12.08.2010

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


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

Из раздела waitpid сайта справочная страница perlfunc:

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

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

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

person Chris Johnsen    schedule 12.08.2010
comment
+1 waitpid вне обработчика SIGCHLD также эффективно избавляет от зомби - person mob; 12.08.2010