2

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

我有一个 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,然后基本上坐在自旋锁中,试图获得结果过程。最终,它成功了,然后使用 init_bash() 启动了一个新的 bash 进程。

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

此外:

  • Fork 在程序中的一个地方被调用,在 init_bash 中。
  • 检查会阻止调用 init_bash,除非在程序启动时和调用上述函数之后调用。

想法?

4

1 回答 1

1

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

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

This article有一个答案,建议不应使用SIGKILL。

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

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

于 2012-10-15T12:29:04.420 回答