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

gph*_*lip 2 posix process

我在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 )
Run Code Online (Sandbox Code Playgroud)

通过此实现,执行将在waitpid上停止.如果我删除waitpid,孩子们继续运行.

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

非常感谢提前!

chi*_*ill 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;
}
Run Code Online (Sandbox Code Playgroud)

如果您看到父母挂在waitpid,则表示孩子没有退出.尝试使用调试器附加以查看子项被阻止的位置,或者更轻松地运行程序strace(1).你如何清理你的pid阵列?确保孩子没有尝试waitpid使用pid参数<= 0进行调用.确保孩子没有阻止或忽略信号.