2010-08-12 6 views
7

Ich versuche, einen Socket-Server, der für jede Verbindung Gabeln schreibt. Ich bin erfolgreich gewesen, abgesehen von einem kleinen Vorbehalt: Meine Kind-Prozesse verwenden Net: OpenSSH-> capture2(), was erfordert, dass $ SIG {CHLD} nicht auf IGNORE oder auf einen benutzerdefinierten Signal-Handler gesetzt wird. Wie kann ich meine Kinder ernten, ohne den Signalhandler zu setzen oder den Parent-Prozess mit wait oder waitpid zu verlangsamen?

Hier ist mein Server-Code:

my $sock = new IO::Socket::INET (
    LocalHost => 'localhost', 
    LocalPort => '1337', 
    Proto  => 'tcp', 
    Listen  => SOMAXCONN, 
    Reuse  => 1, 
); 
die "Could not create socket: $!\n" unless $sock; 

my $new_client, $pid; 

while($new_client = $sock->accept()){ 

    next if $pid = fork; 
    die "fork: $!" unless defined $pid; 

    close $sock; 

    while(<$new_client>) { 
     #do Net::OpenSSH stuff 
    } 

    exit; 

} continue { 
    close $new_client; 
} 

Wenn ich den Code verwenden, wie oben, funktioniert alles gezeigt, aber ich mit einem Bündel von Zombie-Prozesse enden. Wenn ich

local $SIG{CHLD} = 'IGNORE'; 

die Zombies hinzufügen, werden geerntet, aber der Net :: OpenSSH-> capture2() -Methode hat einen Return-Code vermasselt. Ich nehme an, mein Signalhandler stört einige benutzerdefinierte Handler, die Net :: OpenSSH benötigt, um richtig zu funktionieren?

Antwort

11

Gehen Sie voran und legen Sie einen SIGCHLD Handler in den übergeordneten Prozess, aber es in den untergeordneten Prozesse deaktivieren - zum Beispiel eine local $SIG{CHLD} unmittelbar nach dem fork Anruf setzen.

In den untergeordneten Prozessen stammen die SIGCHLD-Ereignisse aus den Methoden Net::OpenSSH und das Modul Net::OpenSSH verarbeitet diese Ereignisse.

Im übergeordneten Prozess kommen SIGCHLD-Ereignisse von Ihren untergeordneten Prozessen. Das sind genau die Ereignisse, an denen Sie interessiert sind und die, die Sie behandeln müssen, um Zombies zu verhindern.

8

Wenn Sie nie Kinder ignorieren und Net :: OpenSSH im selben Prozess verwenden müssen, können Sie möglicherweise $SIG{CHLD} = 'IGNORE' in den Prozessen verwenden, die Net :: OpenSSH nicht verwenden (z. B. der Einzelserverprozess, der sich ausschaltet " auto-reapped "children") und auf $SIG{CHLD} = 'DEFAULT' in den Prozessen zurücksetzen, die Net :: OpenSSH verwenden (z. B. die untergeordneten Elemente des Servers).


Alternativ können Sie eine blockierungs waitpid in einer Schleife um nach jeder neuen Client-Verbindung verwendet werden. Sie können immer noch mit einem oder mehreren Zombies herumhängen, aber sie werden alle bei der nächsten Verbindung geerntet. Wenn du zur Verwendung von select (oder ähnlichem IO::Select) wechselst, kannst du eine Obergrenze für die "Lebenszeit" der Zombies festlegen, indem du eine Auswahl an deinem Horch-Socket machst und nach jedem Timeout eine Runde Non-Blocking-Zombies erschaffst Sowie jede "socket ready" Rückkehr.

Vom waitpid section von perlfunc manpage:

Wenn Sie sagen,

use POSIX ":sys_wait_h"; 
#... 
do { 
    $kid = waitpid(-1, WNOHANG); 
} while $kid > 0; 

dann können Sie für alle anstehenden Zombie-Prozesse eine nicht-blockierende Wartezeit tun. Non-blocking wait ist auf Maschinen verfügbar, die entweder die Systemaufrufe waitpid (2) oder wait4 (2) unterstützen.

+1

+1 'waitpid' außerhalb eines' SIGCHLD' Handlers ist auch effektiv, um Zombies loszuwerden – mob

Verwandte Themen