2012-05-14 96 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