2012-05-14 97 views
1

我使用PROC ::队列管理一堆子进程,我解雇他们过这样的:如何在perl中的父进程中获取死亡的子进程的PID?

if(Proc::Queue::running_now() < $kids) 
    { 
    logger("Starting another server process..."); 
    my $newprocessid = Proc::Queue::run_back(\&serverprocess); 
    logger("New child process id: $newprocessid"); 
    $childprocessids{$newprocessid} = 1; 
    } 

所以现在我有孩子进程ID的哈希,我可以杀了当父进程发送一个kill SIGTERM。

但是如果孩子的过程在外面被杀死了怎么办?父进程知道要启动另一个孩子来弥补丢失的孩子,并且新的孩子PID结束在我的散列中,但父母如何找出死亡孩子的PID以将其从散列中移除,所以我以后不要尝试并杀死它?

我可以设置$SIG{CHLD},但我看不到如何让子PID从我的散列中删除它。

回答

4

您可能会将父母及其子女放在自己的过程组中,并通过向父母发送信号来杀死整个家庭。

根据问题的性质,您可能愿意将kill带走(McManus先生!),并且由于每个尝试kill的故障而生活在已经死亡的子进程上。

如果父进程除了跟踪孩子什么也不做,只需要一个简单的waitpid循环即可。

while ((my $kid = waitpid -1, 0) > 0) { 
    warn "$0: [$$] reaped $kid\n"; 
    delete $kid{$kid}; 
} 

如果你有在父进程等处理,在第二个参数WNOHANG位设置为waitpid告诉系统调用不阻塞。这可以让你定期收获僵尸孩子,然后回到其他处理。

为了演示的目的,假设我们启动了一群昏昏欲睡的孩子。

#! /usr/bin/env perl 

use strict; 
use warnings; 

use 5.10.0; # for defined-or 

my %kid; 

for (1 .. 5) { 
    my $pid = fork // die "$0: fork: $!"; #/fix SO hilighting 
    if ($pid == 0) { 
    warn "$0: [$$] sleeping...\n"; 
    sleep 10_000; 
    exit 0; 
    } 
    else { 
    $kid{$pid} = 1; 
    warn "$0: [$$] forked $pid\n"; 
    } 
} 

然后从外部模拟杀敌,我们叉另一个孩子随意摘掉它的兄弟姐妹的其余部分。

my $pid = fork // die "$0: fork: $!"; 
if ($pid == 0) { 
    warn "$0: [$$] The killer awoke before dawn.\n"; 
    while (keys %kid) { 
    my $pid = (keys %kid)[rand keys %kid]; 
    warn "$0: [$$] killing $pid...\n"; 
    kill TERM => $pid or warn "$0: [$$] kill $pid: $!"; 
    delete $kid{$pid}; 
    sleep 1; 
    } 
    exit 0; 
} 

现在从上面的循环读取讣告。

while ((my $kid = waitpid -1, 0) > 0) { 
    warn "$0: [$$] reaped $kid\n"; 
    delete $kid{$kid}; 
} 

仔细检查,没有人活着出来。

if (keys %kid) { 
    my $es = keys %kid == 1 ? "" : "es"; 
    die "$0: unkilled process$es:\n", 
     map " - $_\n", keys %kid; 
} 

输出:

./waitpid-demo: [1948] forked 7976 
./waitpid-demo: [7976] sleeping... 
./waitpid-demo: [1948] forked 7244 
./waitpid-demo: [7244] sleeping... 
./waitpid-demo: [1948] forked 4776 
./waitpid-demo: [4776] sleeping... 
./waitpid-demo: [1948] forked 4304 
./waitpid-demo: [4304] sleeping... 
./waitpid-demo: [1948] forked 7908 
./waitpid-demo: [7908] sleeping... 
./waitpid-demo: [5144] The killer awoke before dawn. 
./waitpid-demo: [5144] killing 7908... 
./waitpid-demo: [1948] reaped 7908 
./waitpid-demo: [5144] killing 7976... 
./waitpid-demo: [1948] reaped 7976 
./waitpid-demo: [5144] killing 4776... 
./waitpid-demo: [1948] reaped 4776 
./waitpid-demo: [5144] killing 4304... 
./waitpid-demo: [1948] reaped 4304 
./waitpid-demo: [5144] killing 7244... 
./waitpid-demo: [1948] reaped 7244 
./waitpid-demo: [1948] reaped 5144
+0

不是祖父母,只是父母。在工作中会出现外部因素,有人可能会杀死其中一个子进程。这将发生意外或故意,我只是想确保我不会意外地杀死那些不是我的东西,如果pid被重用。我喜欢过程组是最好的选择。 – stu

+0

在这台机器上还有其他真正(也是更重要)的东西在运行,意外杀死其中一个将是一件非常重要的事情。 – stu

+0

@stu查看更新的答案。 –

0

如果唯一的问题是“从散列中删除它,所以我不尝试,后来杀了它”,你可以尝试以下方法:

# probably you should put this code in a loop for all your process IDs 
$alive = kill 0, $childprocessids{$processId}; 

if ($alive) { 
    # process is still alive, do whatever you want with it 
} 
else { 
    # process is dead, probably killed externally 
    # do whatever you need for this scenario or just delete your hash key 
} 
+1

这里的问题是,如果前一个子进程标识符被其他不是我的孩子的其他东西重用,然后我去执行它(如果我有权限的话),那将是一个糟糕的场景。 – stu

+0

是的,我认为你是对的,可能是罕见的事件,但有一个机会 – ArtM