为什么下面的代码中使用sigprocmask来阻止SIGCHLD的传递

发布于 2024-11-24 20:50:41 字数 2310 浏览 1 评论 0原文

基于 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);
}

这是我的理解:

sigprocmask(SIG_SETMASK, &blockMask, NULL)

调用进程的结果信号集应为blockMask指向的集。

问题

为什么我们要说下面的说法?

如果子进程在 SIGCHLD 之前终止,则阻止 SIGCHLD 以防止其传递 父级开始下面的 sigsuspend() 循环

换句话说,我不明白为什么根据 sigprocmask 语句的给定描述使用 sigprocmask 来阻止 SIGCHLD。

Base on 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);
}

Here is my understanding:

sigprocmask(SIG_SETMASK, &blockMask, NULL)

The resulting signal set of calling process shall be the set pointed to by blockMask.

Question

Why do we say the following statement?

Block SIGCHLD to prevent its delivery if a child terminates before the
parent commences the sigsuspend() loop below

In other words, I don't understand why sigprocmask is used to block SIGCHLD based on the given description of the sigprocmask statement.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情愿 2024-12-01 20:50:41

好吧,我认为评论非常清楚......

每当您调用 fork() 并希望以任何方式与子进程交互时,都需要考虑竞争条件。如果孩子先于父母跑一段时间怎么办?或者反之亦然?

在这种情况下,无法知道父级在调用 fork 之后需要多长时间才能到达对 sigsuspend 的调用。那么,如果分叉的子进程在父进程调用 sigsuspend 之前完成其 sleep 并调用 exit 又会怎样呢?然后父级将收到一个 SIGCHLD,它会忽略它......然后它将调用 sigsuspend,它永远不会返回,因为 SIGCHLD已经交付了。

唯一 100% 的解决方案是在调用 fork 之前阻止 SIGCHLD,然后在进入 sigsuspend 时自动解除阻止。 。 (处理这种竞争条件正是 sigsuspend 需要信号掩码作为参数的原因......如果您尝试在调用 sigsuspend 之前解锁信号,则会出现竞争条件;即信号可能在您开始等待之前被传递,然后进入等待必须是原子的,并且您必须在它可能生成之前阻止任何您想要等待的信号。)

Well, I think the comment is pretty clear...

Whenever you call fork() and want to interact with the child in any way, there are race conditions to consider. What if the child runs for a while before the parent does? Or vice-versa?

In this case, there is no way to know how long the parent will take, following the call to fork, to reach the call to sigsuspend. So what if the forked child finishes its sleep and calls exit before the parent calls sigsuspend? Then the parent will receive a SIGCHLD, which it is ignoring... And then it will call sigsuspend, which will never return because the SIGCHLD has already been delivered.

The only 100% solution is to block SIGCHLD before calling fork, and then atomically unblock it on entrance to sigsuspend. (Handling this sort of race condition is exactly why sigsuspend needs a signal mask as argument... If you tried to unblock the signal before calling sigsuspend, there would be a race condition; i.e. the signal might be delivered before you started waiting. Changing the signal mask and then entering the wait must be atomic, and you must block any signal you want to wait for before it can possibly be generated.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文