7

接続ごとにフォークするソケットサーバーを作成しようとしています。1 つの小さな注意点を除いて成功しました。私の子プロセスは Net:OpenSSH->capture2() を使用しており、これには $SIG{CHLD} が IGNORE またはカスタム シグナル ハンドラに設定されていないことが必要です。シグナルハンドラを設定したり、親プロセスを wait または waitpid で遅くしたりせずに、どうすれば子を刈り取ることができますか?

ここに私のサーバーコードがあります:

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;
}

上記のコードを使用すると、すべてが機能しますが、一連のゾンビ プロセスが発生します。追加すると

local $SIG{CHLD} = 'IGNORE';

ゾンビはリープされますが、Net::OpenSSH->capture2() メソッド呼び出しの戻りコードがめちゃくちゃです。私のシグナル ハンドラーが、Net::OpenSSH が適切に動作するために必要なカスタム ハンドラーに干渉していると思いますか?

4

2 に答える 2

11

親プロセスで SIGCHLD ハンドラーを設定しますが、子プロセスでは無効にします。たとえば、呼び出しのlocal $SIG{CHLD}直後にa を配置します。fork

子プロセスでは、SIGCHLD イベントがNet::OpenSSHメソッドから発生し、Net::OpenSSHモジュールがそれらのイベントを処理します。

親プロセスでは、SIGCHLD イベントは子プロセスの終了から発生します。これらはまさにあなたが興味を持っているイベントであり、ゾンビを防ぐために処理する必要があるイベントです.

于 2010-08-12T04:15:59.447 に答える
8

子を無視して同じプロセスで Net::OpenSSH を使用する必要がない場合は、Net::OpenSSH を使用しないプロセスで使用できる可能性があります$SIG{CHLD} = 'IGNORE'(たとえば、「自動取得された」子をフォークする単一サーバー プロセス)。 ) そして$SIG{CHLD} = 'DEFAULT'、Net::OpenSSH を使用するプロセス (例えば、サーバーの子プロセス) でそれをリセットします。


waitpidまたは、新しいクライアント接続ごとに、ループでノンブロッキングを使用することもできます。1 つまたは複数のゾンビがうろついている状態になる可能性はありますが、それらは次の接続ですべて取り除かれます。selectusing (またはIO::Selectのようなもの)に切り替えると、リッスンしているソケットで選択を行い、タイムアウトのたびにノンブロッキング ゾンビ リーピングのラウンドを実行することで、ゾンビの「寿命」に上限を設定できます。すべての「ソケット準備完了」リターンと同様に戻ります。

perlfunc マンページwaitpidセクションから:

あなたが言うなら

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

次に、保留中のすべてのゾンビプロセスに対して非ブロッキング待機を実行できます。ノンブロッキング待機は、waitpid(2) または wait4(2) システムコールをサポートするマシンで利用できます。

于 2010-08-12T04:13:04.317 に答える