从父级信号处理程序中杀死子进程挂起

时间:2011-10-29 10:29:17

标签: process posix

我在posix过程的主题上遇到了问题,我无法解决。

我有一个流程,它会分叉几个孩子(流程树可能很复杂,不仅仅是一个级别)。它还跟踪活跃的儿童的PID。在某些时候,父母会收到一个信号(SIGINT,比方说)。

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

  1. 如果父母没有waitpid()让孩子停止,那么信号似乎永远不会被派遣(僵尸继续运行)
  2. 如果父母在发送给孩子的每次kill()之后等待,它只是挂在那里,孩子似乎忽略了信号
  3. 父级和子级具有相同的信号处理程序,因为它在分叉之前已安装。 这是一个伪代码。

    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,孩子们就会继续运行。

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

    提前多多感谢!

1 个答案:

答案 0 :(得分:6)

您描述的内容应该有效,确实如此,使用以下测试用例:

#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阵列?确保孩子没有尝试使用pid参数为&lt; = 0的呼叫waitpid。确保孩子没有阻止或忽略信号。