2017-08-02 10 views
2

になる私は、次のコードで同時に100個のプロセスを起動しよう:私はsig_handlerSIGCHLD信号をキャッチ同時にフォーク100個のプロセスを、時にはいくつかのプロセスがゾンビ

int cnt = 0; 

void sig_handler(int signo) { 
    pid_t pid; 
    int stat; 
    pid = wait(&stat); 
    cout << "cnt:" << ++cnt << ", pid:" << pid << " signal:" << signo << endl; 
} 

int main() { 
    signal(SIGCHLD, sig_handler); 
    for (int i = 0; i < 100; ++i) { 
     if (fork() == 0) { 
      sleep(1); 
      exit(0); 
     } 
    } 
    printf("wait\n"); 
    while (1); 
} 

、結果が異なります。場合によってはすべてのプロセスがOKを返します。場合によっては1〜4プロセスがゾンビになることがあります。

[[email protected]]$ ./a.out 
wait 
cnt:1, pid:4383 signal:17 
cnt:2, pid:4384 signal:17 
cnt:3, pid:4385 signal:17 
cnt:4, pid:4386 signal:17 
cnt:5, pid:4387 signal:17 
… 
cnt:94, pid:4476 signal:17 
cnt:95, pid:4477 signal:17 
cnt:96, pid:4478 signal:17 
cnt:97, pid:4479 signal:17 
cnt:98, pid:4480 signal:17 

[[email protected] ~]$ ps aux | grep a.out 
Vinllen  4382 96.2 0.0 13896 1084 pts/8 R+ 15:14 0:03 ./a.out 
Vinllen  4481 0.0 0.0  0  0 pts/8 Z+ 15:14 0:00 [a.out] <defunct> 
Vinllen  4482 0.0 0.0  0  0 pts/8 Z+ 15:14 0:00 [a.out] <defunct> 
Vinllen  4493 0.0 0.0 105300 864 pts/9 S+ 15:14 0:00 grep a.out 

私は、複数のプロセスが同時に終了して何かをトリガーする理由を推測します。誰も私に詳細な理由を教えて、この問題を解決する方法を教えてください。

私の理解では、この問題を解決するには、二重フォークとSIGCHLDを無視する2つの効果的な方法があります。しかし、このコードで解決するにはまだwaitを呼び出す。

+1

シグナルハンドラの 'cout'が原因である可能性があります。シグナルハンドラでは、[* async-signal-safe * functions](http://man7.org/linux/man-pages/man7/signal-safety.7.html)のみを呼び出すことになっています。一般に、シグナルハンドラはできるだけ短くして、 'volatile sig_atomic_t'変数を通してメインコードと通信してください。 –

答えて

4

信号はキューに入れられません。もしあなたのコードがwriteのシステムコールにある間にSIGCHLDが発生している場合、プログラムはただ1つの通知を受け取ります。すべての完成子どもたちが享受されるまで

これを処理するための正しい方法は、あなたのハンドラ内のループにある:

コメントで述べたように
void sig_handler(int signo) { 
    pid_t pid; 
    int stat; 
    while ((pid = waitpid(-1, &stat, WNOHANG) > 0) 
    if (WIFEXITED(stat)) 
    { 
     // Don't actually do this: you should 
     // avoid buffered I/O in signal handlers. 
     std::cout << "count:" << ++cnt 
        << ", pid:" << pid 
        << " signal:" << signo 
        << std::endl; 
    } 
} 

、あなたはシグナルハンドラに記載async-signal-safe functionsに固執する必要があります。バッファされたI/O(std::coutの使用を含む)は、シグナルハンドラが内部構造を操作している間に呼び出される可能性があるため、危険です。問題を回避する最良の方法は、volatile sig_atomic_t変数を使用してメインコードとの通信に限定することです。

+0

これは正解であると確信しているので、シグナルハンドラで 'cout'を使うのはC' stdio'関数を使うのと同じくらい安全ではないと思いますので、ここではmainコードはいくつかのI/Oも行っています...刈り取りと印刷のループ全体をハンドラの外側に移動する必要があります。 –

+0

私はこれに基づいて 'sig_handler'プロセスを呼び出す別の質問をしていますか?または子プロセスですか?または父親のプロセスの新しいスレッド? – vinllen

+0

@Felix: 'write'は安全関数リストにリストされています。他の 'std :: cout'が行うこと(状態の維持など)は、STL実装の通常の再入可能制約の影響を受けます。 –

関連する問題