基于http://man7.org/tlpi/code/online/book/procexec/multi_SIGCHLD.c.html
int
main(int argc, char *argv[])
{
int j, sigCnt;
sigset_t blockMask, emptyMask;
struct sigaction sa;
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s child-sleep-time...\n", argv[0]);
setbuf(stdout, NULL); /* Disable buffering of stdout */
sigCnt = 0;
numLiveChildren = argc - 1;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sigchldHandler;
if (sigaction(SIGCHLD, &sa, NULL) == -1)
errExit("sigaction");
/* Block SIGCHLD to prevent its delivery if a child terminates
before the parent commences the sigsuspend() loop below */
sigemptyset(&blockMask);
sigaddset(&blockMask, SIGCHLD);
if (sigprocmask(SIG_SETMASK, &blockMask, NULL) == -1)
errExit("sigprocmask");
for (j = 1; j < argc; j++) {
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Child - sleeps and then exits */
sleep(getInt(argv[j], GN_NONNEG, "child-sleep-time"));
printf("%s Child %d (PID=%ld) exiting\n", currTime("%T"),
j, (long) getpid());
_exit(EXIT_SUCCESS);
default: /* Parent - loops to create next child */
break;
}
}
/* Parent comes here: wait for SIGCHLD until all children are dead */
sigemptyset(&emptyMask);
while (numLiveChildren > 0) {
if (sigsuspend(&emptyMask) == -1 && errno != EINTR)
errExit("sigsuspend");
sigCnt++;
}
printf("%s All %d children have terminated; SIGCHLD was caught "
"%d times\n", currTime("%T"), argc - 1, sigCnt);
exit(EXIT_SUCCESS);
}
这是我的理解:
调用过程的结果信号集应为 blockMask 指向的信号集。
问题
我们为什么要说以下话?
换句话说,我不明白为什么基于给定的sigprocmask语句描述,使用sigprocmask来阻止SIGCHLD。
最佳答案
好吧,我认为评论很清楚...
每当您调用fork()并希望以任何方式与 child 互动时,都需要考虑种族条件。如果 child 在 parent 之前跑了一段时间怎么办?或相反亦然?
在这种情况下,无法知道父级在调用fork
之后要花费多长时间才能到达对sigsuspend
的调用。那么,如果 fork 的 child 完成其sleep
并在父级调用exit
之前调用sigsuspend
怎么办?然后,父级将收到它正在忽略的SIGCHLD
...然后它将调用sigsuspend
,由于SIGCHLD
已经交付,它将永远不会返回。
唯一的100%解决方案是在调用SIGCHLD
之前先阻止fork
,然后在进入sigsuspend
时原子地对其进行解锁。 (处理这种竞争条件正是sigsuspend
需要信号掩码作为参数的原因...如果您在调用sigsuspend
之前尝试取消阻止信号,则会出现竞争条件;即,信号可能在开始等待之前就已交付。信号屏蔽,然后进入等待必须是原子的,并且您必须阻塞要等待的任何信号,然后才能生成该信号。)