为什么信号处理程序中的waitpid需要循环?

时间:2017-08-22 05:31:07

标签: c linux unix process

我在电子书中读到waitpid(-1,& status,WNOHANG)应该放在while循环中,这样如果多个子进程同时退出,它们都会被收获。

我通过同时创建和终止2个子进程并通过waitpid收到它而不使用循环来尝试这个概念。这些都被收获了。

问题是,将waitpid放在循环中是否非常必要?

#include<stdio.h>
#include<sys/wait.h>
#include<signal.h>

int func(int pid)
{
  if(pid < 0)
    return 0;
  func(pid - 1);
}

void sighand(int sig)
{
  int i=45;
  int stat, pid;
  printf("Signal caught\n");
  //while( (
  pid = waitpid(-1, &stat, WNOHANG);
  //) > 0){
  printf("Reaped process %d----%d\n", pid, stat);
  func(pid);
}

int main()
{
  int i;
  signal(SIGCHLD, sighand);
  pid_t child_id;

  if( (child_id=fork()) == 0 )  //child process
  {
    printf("Child  ID %d\n",getpid());
    printf("child exiting ...\n");
  }
  else
  {
    if( (child_id=fork()) == 0 ) //child process
    {
      printf("Child ID %d\n",getpid());
      printf("child exiting ...\n");
    }
   else
    {
      printf("------------Parent with  ID %d \n",getpid());
      printf("parent exiting ....\n");
      sleep(10);
      sleep(10);
    }
  }
}

1 个答案:

答案 0 :(得分:5)

好的,我会详细说明。

每次对waitpid的调用都会收获一个,只有一个孩子。由于您将调用放在信号处理程序中,因此无法保证在执行第一个信号处理程序之前第二个子项将退出。对于两个正常的进程(待处理的信号将在您完成时处理),但是对于更多,可能是两个孩子将在您还在处理另一个孩子时完成。由于信号没有排队,您将错过通知。

如果发生这种情况,你就不会收获所有孩子。为了避免这个问题,引入了循环建议。如果您希望看到它发生,请尝试与更多孩子一起运行您的测试。你跑的越多,你就越有可能看到这个问题。

在此之后,让我们谈谈其他一些问题。

首先,您的信号处理程序调用{​​{1}}。这是一个重要的禁忌。很少有函数是信号处理程序安全的,printf绝对不是一个。您可以尝试使信号处理程序更安全,但更安全的方法是放入一个仅设置标志的信号处理程序,然后在主程序的流程中执行实际的printf调用。 / p>

由于您的主流通常是调用select / epoll,因此请务必查找waitpselect,并了解它们的作用以及需要它们的原因。

更好(但特定于Linux),查找epoll_pwait。您可能根本不需要信号处理程序。

已编辑添加: 循环不会改变将两个信号传递合并到一个处理程序调用中的事实。它所做的是这一个调用处理所有未决事件。

当然,一旦出现这种情况,您必须使用signalfd。导致信号合并的相同工件也可能导致您处理尚未传递信号的事件。

如果发生这种情况,那么一旦你的第一个信号处理程序存在,它将再次被调用。但是,这次没有未决事件(因为事件已经被循环提取)。如果您未指定WNOHANG,则WNOHANG阻止,程序将无限期停留。