避免在C++中产生僵尸进程

Avoiding the production of zombie processes in C++

本文关键字:僵尸 进程 C++      更新时间:2023-10-16

非常奇怪的bug,也许有人会看到我缺少的东西。

我有一个C++程序,它派生出一个bash shell,然后将命令传递给它

命令将定期包含无意义的内容,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发送到shell,然后本质上坐在一个spinlock中,试图获得结果过程。最终,它成功了,然后用init_bash()启动了一个新的bash进程。

至少,这是应该发生的事情。相反,子进程的退出状态从未被收集,并且它继续作为僵尸进程存在。尽管如此,父确实退出循环并设法重新启动bash进程,然后继续正常执行。最终生成了太多僵尸,系统中的pid也用完了。

另外:

  • Fork在程序中的init_bash内的一个位置被调用
  • 检查可防止init_bash被调用,除非在程序启动时和调用上述函数后调用一次

想法?

我读到的文章表明,僵尸进程的原因是子进程退出,而父进程从不收集子进程的退出。

本文提供了几种从命令行杀死僵尸进程的方法。一种技术是使用SIGKILL之外的其他信号,例如SIGTERM。

本文给出的答案表明不应该使用SIGKILL。

其中一种技术是杀死父进程,从而也杀死其子进程,包括任何僵尸。作者指出,在操作系统重新启动之前,似乎有一些子进程仍然是僵尸。

您没有提及用于将命令传达给子进程的机制。然而,一种选择可能是通过断开子进程与其父进程的连接来释放子进程,类似于终端进程的子进程与终端会话的连接。这样,孩子将成为自己的过程,如果出现问题,可能会退出而不会变成僵尸。