Почему sigprocmask используется для блокировки доставки SIGCHLD в следующем коде - PullRequest
6 голосов
/ 20 июля 2011

База на 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, чтобы предотвратить его доставку, если ребенок прерывает работу до parent начинает цикл sigsuspend () ниже

Другими словами, я не понимаю, почему sigprocmask используется для блокировки SIGCHLD на основе данного описания оператора sigprocmask.

1 Ответ

9 голосов
/ 20 июля 2011

Ну, я думаю, что комментарий довольно ясен ...

Когда вы вызываете fork () и хотите каким-либо образом взаимодействовать с ребенком, существуют условия гонки, которые необходимо учитывать.Что делать, если ребенок бежит некоторое время, прежде чем родитель?Или наоборот?

В этом случае невозможно узнать, сколько времени потребуется родителю после вызова на fork, чтобы дозвониться до sigsuspend.Так что, если разветвленный потомок заканчивает свой sleep и вызывает exit до того, как родительский вызов sigsuspend?Тогда родитель получит SIGCHLD, который он игнорирует ... И затем он вызовет sigsuspend, который никогда не вернется, потому что SIGCHLD уже доставлен.

Только 100%решение состоит в том, чтобы заблокировать SIGCHLD до вызова fork и затем атомно разблокировать его при входе в sigsuspend.(Обработка такого типа состояния гонки именно поэтому sigsuspend нуждается в маске сигнала в качестве аргумента ... Если вы попытались разблокировать сигнал перед вызовом sigsuspend, возникло бы состояние гонки; т.е. сигнал мог быть доставлен до васожидание. Изменение маски сигнала и затем ввод ожидания должны быть атомарными, и вы должны заблокировать любой сигнал, который хотите подождать, прежде чем его можно будет сгенерировать.)

...