非常に奇妙なバグです。おそらく誰かが私が見逃しているものを見るでしょう。
bash シェルからフォークしてコマンドを渡す C++ プログラムがあります。
定期的に、コマンドに意味のないものが含まれ、bash プロセスがハングします。semtimedwait を使用してこれを検出し、次のような小さな関数を実行します。
if (kill(*bash_pid, SIGKILL)) {
cerr << "Error sending SIGKILL to the bash process!" << endl;
exit(1);
} else {
// collect exit status
long counter = 0;
do {
pid = waitpid(*bash_pid, &status, WNOHANG);
if (pid == 0) { // status not available yet
sleep(1);
}
if(counter++ > 5){
cerr << "ERROR: Bash child process ignored SIGKILL >5 sec!" << endl;
}
} while (pid != *bash_pid && pid != -1);
if(pid == -1){
cerr << "Failed to clean up zombie bash process!" << endl;
exit(1);
}
// re-initialized bash process
*bash_pid = init_bash();
}
waitpid の動作を正しく理解していると仮定すると、これは最初に SIGKILL をシェルに送信し、次に本質的にスピンロックに座って、結果のプロセスを取得しようとします。最終的には成功し、新しい bash プロセスが init_bash() で開始されます。
少なくとも、そうあるべきです。代わりに、子プロセスの終了ステータスは収集されず、ゾンビ プロセスとして存在し続けます。それにもかかわらず、親はループを終了し、bash プロセスを再起動することに成功し、通常の実行を続行します。最終的に、生成されるゾンビが多すぎて、システムは pid を使い果たします。
さらに:
- fork は、init_bash 内のプログラム内の 1 か所だけで呼び出されます。
- チェックは、プログラムの開始時と上記の関数の呼び出し後に 1 回を除いて、init_bash が呼び出されるのを防ぎます。
考え?