我有一个程序正在分叉给一个孩子去做一些工作,但是此时我一次只生一个孩子。我正在wait()
等待孩子完成工作,是否还需要做任何事情SIGCHLD
(例如禁用处理程序)?
在我的情况我得到的值,EINTR
在errno
这使我觉得我需要屏蔽SIGCHLD
。
概括地说,这是程序:
fork()
execlp()
参加工作计划wait()
要让孩子完成POSIX谈到SIG_IGN
(严格在XSI扩展说明下):
如果将SIGCHLD信号的操作设置为SIG_IGN,则调用进程的子进程终止时不应转换为僵尸进程。如果调用进程随后等待其子进程,并且该进程没有未等待的子进程转换为僵尸进程,则它将阻塞直到其所有子进程终止,并且wait(),waitid()和waitpid()应失败,并将errno设置为[ECHILD]。
并在描述中<signal.h>
说,对默认的信号配置SIGCHLD
是“忽略”(代码“I”)。但是,它的SIG_IGN
SIG_DFL
行为是“忽略”信号。永远不会产生信号。这与SIG_IGN
行为不同。
因此,您不必设置信号处理程序,但是您不应该获取有关该过程的信息-一旦没有子级,您应该得到一个错误。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static siginfo_t sig_info;
static volatile sig_atomic_t sig_num;
static void *sig_ctxt;
static void catcher(int signum, siginfo_t *info, void *vp)
{
sig_num = signum;
sig_info = *info;
sig_ctxt = vp;
}
static void set_handler(int signum)
{
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = catcher;
sigemptyset(&sa.sa_mask);
if (sigaction(signum, &sa, 0) != 0)
{
int errnum = errno;
fprintf(stderr, "Failed to set signal handler (%d: %s)\n", errnum, strerror(errnum));
exit(1);
}
}
static void prt_interrupt(FILE *fp)
{
if (sig_num != 0)
{
fprintf(fp, "Signal %d from PID %d\n", sig_info.si_signo, (int)sig_info.si_pid);
sig_num = 0;
}
}
static void five_kids(void)
{
for (int i = 0; i < 5; i++)
{
pid_t pid = fork();
if (pid < 0)
break;
else if (pid == 0)
{
printf("PID %d - exiting with status %d\n", (int)getpid(), i);
exit(i);
}
else
{
int status = 0;
pid_t corpse = wait(&status);
printf("Child: %d; Corpse: %d; Status = 0x%.4X\n", pid, corpse, (status & 0xFFFF));
prt_interrupt(stdout);
fflush(0);
}
}
}
int main(void)
{
printf("SIGCHLD set to SIG_IGN\n");
signal(SIGCHLD, SIG_IGN);
five_kids();
printf("SIGCHLD set to catcher()\n");
set_handler(SIGCHLD);
five_kids();
return(0);
}
该fflush(0);
调用可确保刷新标准输出(尤其是标准输出),如果将示例程序的输出通过管道传输到另一个程序,则这很重要。
示例代码的输出与理论相符-但需要一些解释。
SIGCHLD set to SIG_IGN
PID 4186 - exiting with status 0
SIGCHLD set to SIG_IGN
Child: 4186; Corpse: -1; Status = 0x0000
PID 4187 - exiting with status 1
Child: 4187; Corpse: -1; Status = 0x0000
PID 4188 - exiting with status 2
Child: 4188; Corpse: -1; Status = 0x0000
PID 4189 - exiting with status 3
Child: 4189; Corpse: -1; Status = 0x0000
PID 4190 - exiting with status 4
Child: 4190; Corpse: -1; Status = 0x0000
SIGCHLD set to catcher()
PID 4191 - exiting with status 0
SIGCHLD set to catcher()
Child: 4191; Corpse: -1; Status = 0x0000
Signal 20 from PID 4191
Child: 4192; Corpse: 4191; Status = 0x0000
PID 4192 - exiting with status 1
Child: 4193; Corpse: 4192; Status = 0x0100
Signal 20 from PID 4192
PID 4193 - exiting with status 2
Child: 4194; Corpse: 4193; Status = 0x0200
Signal 20 from PID 4193
PID 4194 - exiting with status 3
Child: 4195; Corpse: 4194; Status = 0x0300
Signal 20 from PID 4194
PID 4195 - exiting with status 4
输出的第一部分与理论完全一致。调用代码不会获得有关已死亡子代的任何信息,只是没有等待等待的已死亡子代(左)。
输出的第二部分也与理论相符,但是看起来子代并没有在父代之前执行,因此父代的第一部分wait()
没有等待死亡的子代,它返回-1。随后的呼叫得到了各种各样的孩子,但是一个逃生未等待(但是尸体将被系统清理)。如果使用的代码waitpid()
如:pid_t corpse = waitpid(pid, &status, 0);
,它将依次等待每个孩子。
根据上面有关“忽略”的修改后的注释,某些注释可能需要修订。待定-目前时间已过。
3 GHz Intel Core 2 Duo,GCC 4.6.0、64位编译器上的Mac OS X 10.8.4:
gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition sigchld.c -o sigchld
这是一些现有的代码很快适应了测试的行为SIGINT
和pause()
。它可能包含一些多余的材料。您不必wait()
在信号处理程序中致电或其亲戚之一;不过,如果您愿意,也可以这样做。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句