【问题标题】:Why isn't my Perl program reaping children processes after fork?为什么我的 Perl 程序在 fork 后没有收获子进程?
【发布时间】:2010-10-30 04:41:41
【问题描述】:

我一直在尝试使用 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

从结果中可以看出,成功扫描的线程打印“up”消息,干净地退出并被父进程收割。同时,其他 251 个线程悬空连接到 '/sbin/init',这可以从快速的 'ps -ef' 列表中看出。如果我在退出语句之前的子处理块中添加一个'print "Child: $currIPending\n"',我会在我的 perl 脚本退出“之后”从我的终端上的剩余 251 个进程中获得输出。

这里发生了什么?我认为 $SIG{CHLD} 子例程与 waitpid 循环相结合将获取所有子进程并确保系统中没有僵尸/悬空进程。

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

【问题讨论】:

  • 您的循环从 1-254(253?) 而不是 0-255 的任何原因?
  • Matt Boehm 255 通常是广播地址(假设是 /24 子网)。 0 也有特殊含义(在 /24 中),但我忘记它是什么了。
  • Matt,全 0 保留给网络 ID,全 1 保留广播地址。
  • 在 CIDR 网络中,有两个保留地址,一个本地部分全为零(对于 /24,xyz0)是网络本身的地址,另一个是本地地址部分全部(对于/24,xyz255),这是广播地址
  • 也许你应该试试 NMap。

标签: perl ipc fork zombie-process


【解决方案1】:

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

#!/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;
}

【讨论】:

  • 我尝试将其设置为“IGNORE”,但大多数线程仍然悬空,如上所述。
  • 我一直在寻找一种简单的方法来做到这一点......最后的那个小 for 循环就是我所需要的......非常感谢
【解决方案2】:

看看Parallel::ForkManager。它会为您处理所有这些小细节。

【讨论】:

    【解决方案3】:

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

    就拥有一组 N 个线程而言,为什么不将 0-255 分成块,例如有两个线程,一个从 0-127 到一个从 128-255 ?为简单起见,我会使用 2 的倍数作为线程数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-03
      • 2023-03-16
      • 2020-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多