基于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之前尝试取消阻止信号,则会出现竞争条件;即,信号可能在开始等待之前就已交付。信号屏蔽,然后进入等待必须是原子的,并且您必须阻塞要等待的任何信号,然后才能生成该信号。)

10-08 04:57