【问题标题】:Killing child processes from parent's signal handler hangs从父母的信号处理程序中杀死子进程挂起
【发布时间】:2011-10-29 10:29:17
【问题描述】:

我在 posix 进程方面遇到问题,无法解决。

我有一个派生多个子进程的进程(进程树可能很复杂,而不仅仅是一层)。它还跟踪活跃儿童的 PID。在某些时候,父母会收到一个信号(比如说 SIGINT)。

在 SIGINT 的信号处理程序中,它遍历子进程列表并向它们发送相同的信号以防止僵尸。现在问题来了

  1. 如果父进程没有 waitpid() 停止子进程,则信号似乎永远不会发送(僵尸继续运行)
  2. 如果父母在每次 kill() 发送给孩子后都等待,它只是挂在那里,孩子似乎忽略了信号

父子节点具有相同的信号处理程序,因为它是在分叉之前安装的。 这是一个伪代码。

signal_handler( signal )
    foreach child in children
        kill( child, signal )
        waitpid( child, status )

    // Releasing system resources, etc.
    clean_up()

    // Restore signal handlers.
    set_signal_handlers_to_default()

    // Send back the expected "I exited after XY signal" to the parent by
    // executing the default signal handler again.
    kill( getpid(), signal )

有了这个实现,执行会在 waitpid 上停止。如果我删除 waitpid,孩子们会继续运行。

我的猜测是,除非信号处理程序已经结束,否则从它发送的信号不会分派给孩子。但是,如果我省略了等待,为什么他们不派出呢?

提前非常感谢!

【问题讨论】:

  • 看起来孩子们被困在信号处理程序中。 children 在孩子们身上有什么价值? cleanup 是做什么的?
  • childen in children 如果是叶子则为空(否则包含其子进程)。就在分叉之后,我清空了孩子中的这个数组。为了调试,cleanup 是 viod(空)。
  • 出于好奇,SIGCHLD的性格是什么?
  • 不管信号处理程序如何,在同一代码执行中还有另一个等待,它等待所有子进程停止(不仅仅是一个 pid),但没有设置 SIGCHLD 的信号处理程序。跨度>
  • 如果到目前为止还没有人提出令人信服的解释——我当然没有——你可能会发布一些代码。您能否将代码缩减为重现问题的最小样本?

标签: process posix


【解决方案1】:

您所描述的内容应该有效,并且确实可以使用以下测试用例:

#include <stdio.h>
#include <unistd.h>
#include <signal.h>

#define NCHILDREN 3
pid_t child [NCHILDREN];

struct sigaction sa, old;

static void
handler (int ignore)
{
  int i;

  /* Kill the children.  */
  for (i = 0; i < NCHILDREN; ++i)
    {
      if (child [i] > 0)
        {
          kill (child [i], SIGUSR1);
          waitpid (child [i], 0, 0);
        }
    }

  /* Restore the default handler.  */
  sigaction (SIGUSR1, &old, 0);

  /* Kill self.  */
  kill (getpid (), SIGUSR1);
}

int
main ()
{
  int i;

  /* Install the signal handler.  */
  sa.sa_handler = handler;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction (SIGUSR1, &sa, &old);

  /* Spawn the children.  */
  for (i = 0; i < NCHILDREN; ++i)
    {
      if ((child [i] = fork ()) == 0)
        {
          /* Each of the children: clear the array, wait for a signal
             and exit.  */
          while (i >= 0)
            child [i--] = -1;
          pause ();
          return 0;
        }
    }

  /* Wait to be interrupted by a signal.  */
  pause ();
  return 0;
}

如果看到父级挂在waitpid,则表示子级还没有退出。尝试使用调试器附加以查看孩子被阻止的位置,或者更简单,使用strace(1) 运行程序。你如何清理你的pid数组?确保孩子们没有尝试调用waitpid 且 pid 参数为

【讨论】:

  • 这对我也有用。你甚至不需要 waitpid() 调用。没有 waitpid() 调用僵尸仍然运行的原因是因为孩子已经被阻塞了。这就解释了为什么当 waitpid() 就位时主进程挂起。
猜你喜欢
  • 2012-11-01
  • 2019-06-14
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 2012-03-12
  • 2016-06-05
  • 2014-09-16
  • 1970-01-01
相关资源
最近更新 更多