关于SIGCHLD和子进程的返回值

前言:这个帖子主体毫无价值,要是谁不小心看到了,直接跳到后记吧,唯一有用的东西在那里

======================================================

在写一个daemon,其中会fork一些子进程执行命令,一开始我不关心这些命令的返回值,并且有些命令的执行时间会比较长。于是捕获了SIGCHLD,通过while (waitpid (-1, &status, WNOHANG) > 0)来回收进程;

后来需要取得某些命令的返回值,首先尝试system (),命令能够正确执行,可是总返回-1。system()函数比较复杂,屏蔽了几个信号,fork了两次,可能会有副作用。

于是考虑别的方案,最后测试发现,对于不关心返回值的,可以直接fork exec,让signal handler来回收子进程。对于关心返回值的,fork, 子进程exec以后,父进程wait得到返回状态。SIGCHLD不能设置为SIG_IGN,这会导致wait取不到状态。

马上又出现了一个新的问题,对于需要取得返回值得命令,fork之后有一个waitpid,signal handler中也有一个waitpid,子进程退出之后,到底执行了哪个waitpid?这是一种竞争关系,还是有固定的次序?
然后google了一下,发现了这个页面

http://www.mail-archive.com/gimp-developer@scam.xcf.berkeley.edu/msg03400.html

Raphael Quinet写了一个测试程序,用来测试waitpid和signal handler哪个先执行,测试发现Linux 和 IRIX 上waitpid 优先于signal handler,而其他大多数的unix系统则是signal handler优先于waitpid。在Linux平台上,刚好符合我的需求。

问题又来了,假如这个程序需要移植到solaris,该怎么做? Interesting…

 

附Raphael的测试程序:

#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int pid;
int sig_pid = 9999;
int sig_status = 9999;
int main_pid = 9999;
int main_status = 9999;

static void
sigchld_handler (int signum)
{
  sig_pid = waitpid (pid, &sig_status, WNOHANG);
}

int
main (int argc, char *argv[])
{
  int ret;
  struct sigaction sa;
  struct sigaction osa;

  printf ("installing signal handler...\n");
  sa.sa_handler = sigchld_handler;
  sigfillset (&sa.sa_mask);
  sa.sa_flags = SA_RESTART;
  ret = sigaction (SIGCHLD, &sa, &osa);
  if (ret < 0)
    {
      printf ("cannot set signal handler, bye!\n");
      exit (-1);
    }

  printf ("forking...\n");
  pid = fork ();
  if (pid == 0)
    {
      sleep (1);
      _exit (100);
    }

  printf ("waiting for child %d to exit...\n", pid);
  main_pid = waitpid (pid, &main_status, 0);

  printf ("child %d has exited\n", pid);
  printf ("  sig_pid = %d\n", sig_pid);
  printf ("  sig_status = %d\n", sig_status);
  printf ("  main_pid = %d\n", main_pid);
  printf ("  main_status = %d\n", main_status);
  if (sig_pid < 0)
    {
      if (main_pid < 0)
        printf ("no child status (fork failed?)\n");
      else
        printf ("waitpid got the status before sigchld handler was called\n");
    }
  else
    {
      if (main_pid < 0)
        printf ("sigchld handler was called before waitpid (no status)\n");
      else
        printf ("you seem to have a very interesting OS...\n");
    }
}

=========================================================

后记:这个帖子实在太SB了,

要返回值就fork,wait
不要返回值就fork两次,只wait firstchild
连SIGCHLD都不用管

apue 8.6 罚抄100遍啊

updatedupdated2022-02-222022-02-22