代码之家  ›  专栏  ›  技术社区  ›  aks

为什么我的Perl程序不能在fork之后获取子进程?

  •  4
  • aks  · 技术社区  · 15 年前

    我一直在尝试使用Perl编写一个简单的ping扫描器供内部使用。因为它扫描一个24位的CIDR网络,所以如果脚本在一个线程中运行,则运行时间太长。我已经尝试添加fork功能以加快进程,但是我的 first attempt 因为在任何给定时间只有一个子进程处于活动状态,所以占用的时间几乎相同。

    我读到了关于 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”列表看到。如果我在exit语句之前的子处理块中添加了一个'print'子进程:$currip ending \n“”,我将在“我的perl脚本退出”之后从终端上的其余251个进程中获取输出。

    这是怎么回事?我认为$sig chld子例程加上waitpid循环将获取所有子进程,并确保系统中没有僵尸/悬空进程。

    同样,我也希望能够在任何给定的时间运行特定数量的子进程,例如,“n”子进程同时运行,每当一个退出父进程时,如果需要,会启动另一个子进程,但在任何给定的时刻都不超过“n”子进程。这有可能吗?如果是,我可以得到一些伪代码来帮助指导我吗?

    3 回复  |  直到 15 年前
        1
  •  6
  •   brian d foy    15 年前

    看起来你的父母进程在孩子们之前就已经完成了(因此永远没有机会收获他们)。试试这个:

    #!/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;
    }
    
        2
  •  4
  •   mpeters    15 年前

    看一看 Parallel::ForkManager . 它为你处理所有这些小细节。

        3
  •  3
  •   Matt Boehm    15 年前

    当线程调用ping()时,它会一直尝试ping IP,直到它设置响应为止。要解决此问题,请尝试将超时作为ping()中的第二个参数。现在看起来,剩下的线程继续ping以获取响应,直到得到响应为止。

    至于有一组n个线程,为什么不把0-255分成若干块,例如有两个线程,一个从0-127开始,一个从128-255结束?为了简单起见,我将使用一些2的倍数作为线程数。