Почему моя Perl-программа не пожинает дочерние процессы после fork? - PullRequest
4 голосов
/ 31 мая 2009

Я пытался написать простой пинг-сканер, используя Perl для внутреннего использования. Поскольку он сканирует 24-битную сеть CIDR, выполнение сценария занимает слишком много времени, если он выполняется в одном потоке. Я попытался добавить функциональность форка, чтобы ускорить процесс, но моя первая попытка заняла почти столько же времени, поскольку в каждый момент времени был активен только один дочерний процесс.

Я прочитал о дочерних процессах в документе perlipc , а также в Perl Cookbook и предложил вторую версию:

##Install the CHLD SIG handler
$SIG{CHLD} = \&REAPER;
sub REAPER {
    my $childPID;
    while (( $childPID = waitpid(-1, WNOHANG)) > 0) {
        print "$childPID exited\n";
    }
    $SIG{CHLD} = \&REAPER;
}

my $kidpid;
for (1 .. 254) {
    my $currIP = join ".", (@ipSubset[0,1,2], $_);

    die "Could not fork()\n" unless defined ($kidpid = fork);
    if ($kidpid) {
        #Parent process
        #Does nothing
    } 
    else {
        #Child process
        my $pingConn = Net::Ping->new();    #TCP
        say "$currIP up" if $pingConn->ping($currIP);
        $pingConn->close(); 

        #Nothing else to do
        exit;
    }
}

say "Finished scanning $ipRange/24";

Когда я сканирую свою внутреннюю сеть, вывод:

$perl pingrng2.pl 192.168.1.1
192.168.1.2 up
5380 exited
192.168.1.102 up
192.168.1.100 up
5478 exited
5480 exited
Finished scanning 192.168.1.1/24

Как можно видеть в результате, потоки, которые выполняют успешное сканирование, печатают сообщение «вверх», выходят корректно и получают исходный процесс. Тем временем остальные 251 поток остаются привязанными к / sbin / init, как видно из быстрого списка ps -ef. Если я добавлю «print: Child: $ currIP end \ n» в дочерний блок обработки непосредственно перед оператором выхода, я получу вывод из оставшихся 251 процессов на моем терминале «после» завершения моего perl-скрипта.

Что здесь происходит? Я думал, что подпрограмма $ SIG {CHLD} в сочетании с циклом waitpid пожнет все дочерние процессы и гарантирует, что в системе не останется ни одного зомби / висячих процессов.

В то же время я также хотел бы иметь возможность запускать определенное количество дочерних процессов в любой момент времени, например, 'n' дочерние процессы, запущенные одновременно, каждый раз, когда выходит из родительского процесса, запускается другой дочерний процесс, если необходимо, но не имеет больше чем n детей в любой момент. Это возможно? Если да, могу ли я получить какой-нибудь псевдокод, который поможет мне?

Ответы [ 3 ]

6 голосов
/ 31 мая 2009

Похоже, что ваш родительский процесс завершается раньше, чем дети (и, следовательно, никогда не получает возможности пожинать их). Попробуйте вместо этого:

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

use Net::Ping;

my @ipSubset = (192, 168, 10);

my $i = 0;
my @pids;
for my $octet (1 .. 254) {
    my $currIP = join ".", @ipSubset[0 .. 2], $octet;

    die "Could not fork()\n" unless defined (my $pid = fork);

    #parent saves chlidren's pids and loops again
    if ($pid) {
        push @pids, $pid;
        next;
    } 

    #child process
    my $pingConn = Net::Ping->new;
    say "$currIP up" if $pingConn->ping($currIP);
    $pingConn->close(); 
    exit;
}

#wait on the children
for my $pid (@pids) {
    waitpid $pid, 0;
}
4 голосов
/ 01 июня 2009

Посмотрите на Parallel :: ForkManager . Он позаботится обо всех этих маленьких деталях для вас.

3 голосов
/ 31 мая 2009

Когда поток вызывает ping (), он продолжает пытаться пропинговать IP, пока не установит ответ. чтобы исправить это, попробуйте включить таймаут в качестве второго аргумента в ping (). Похоже, прямо сейчас эти оставшиеся потоки продолжают пинговать ответ, пока не получат его.

Если число потоков установлено N, почему бы не разбить 0-255 на куски, например, имеющие два потока, один из которых идет от 0 до 127, а другой от 128 до 255? Для простоты я бы использовал кратное 2 для вашего количества потоков.

...