前言:这个帖子主体毫无价值,要是谁不小心看到了,直接跳到后记吧,唯一有用的东西在那里
======================================================
在写一个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遍啊