Должны ли дочерние процессы также разблокировать заблокированные сигналы SIGCHLD?

Я пытаюсь понять, как работают сигналы блокировки и разблокировки, и я пытаюсь понять следующий фрагмент кода. В частности, я смотрю на строку 28 (прокомментировано в коде): int a = sigprocmask(SIG_UNBLOCK, &mask, NULL);ака где сигнал разблокирован у ребенка.

В учебнике, из которого я получил код, написано, что в коде используется блокировка сигнала, чтобы программа выполняла функцию добавления (упрощенно printf("adding %d\n", pid);) перед его функцией удаления (упрощено до printf("deleting %d\n", pid);). Это имеет смысл для меня; блокируя SIGCHLD сигнал, затем разблокировать его после выполнения функции добавления, мы гарантируем, что обработчик не вызывается, пока мы не выполним функцию добавления. Однако зачем нам разблокировать сигнал у ребенка? Разве это не устраняет всего смысла блокировки, сразу же разблокируя его, позволяя дочернему элементу удалить до того, как родитель добавит?

Однако выходные данные (описанные после кода) идентичны, независимо от того, закомментирована ли строка или нет, а это означает, что это явно не то, что происходит. В учебнике говорится:

"Обратите внимание, что дети наследуют заблокированный набор своих родителей, поэтому мы должны быть осторожны, чтобы разблокировать SIGCHLD сигнализировать ребенку перед звонком execve".

Но мне все еще кажется, что разблокировка привела бы к вызову обработчика. Что именно делает эта линия?

void handler(int sig) {
    pid_t pid;
    printf("here\n");
    while ((pid = waitpid(-1, NULL, 0)) > 0); /* Reap a zombie child */
    printf("deleting %d\n", pid); /* Delete the child from the job list */
}

int main(int argc, char **argv) {
    int pid;
    sigset_t mask;
    signal(SIGCHLD, handler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHLD */

    pid = fork();
    if (pid == 0) {
        printf("in child\n");

        int a = sigprocmask(SIG_UNBLOCK, &mask, NULL); // LINE 28

        printf("a is %d\n",a);
        execve("/bin/date", argv, NULL);
        exit(0);
    }

    printf("adding %d\n", pid);/* Add the child to the job list */
    sleep(5);
    printf("awake\n");

    int b = sigprocmask(SIG_UNBLOCK, &mask, NULL);
    printf("b is %d\n", b);
    sleep(3);

    exit(0);
}

Выходы:

adding 652

in child

a is 0

Wed Apr 24 20:18:04 UTC 2019

awake

here

deleting -1

b is 0

1 ответ

Решение

Однако зачем нам разблокировать сигнал у ребенка? Разве это не устраняет всего смысла блокировки, сразу же разблокируя его, позволяя дочернему элементу удалить до того, как родитель добавит?

Нет. Каждый процесс имеет свою собственную маску сигналов. Новый процесс наследует маску сигналов своего родителя, но только в том же смысле, в котором он наследует содержимое памяти родителя - дочерний элемент получает то, что составляет независимую копию. Его модификации в этой копии не отражаются в копии родителя, и наоборот, после того, как ребенок начинает. Если бы это было не так, то все процессы в системе имели бы одну маску сигналов.

Только родитель не должен получать SIGCLD слишком рано, поэтому только родитель должен блокировать этот сигнал.

[...] В учебнике говорится:

"Обратите внимание, что дети наследуют заблокированный набор своих родителей, поэтому мы должны быть осторожны, чтобы разблокировать сигнал SIGCHLD в ребенке перед вызовом execve".

Но мне все еще кажется, что разблокировка привела бы к вызову обработчика.

Опять же, "наследовать" в смысле наследования копии, а не в том, чтобы разделять одну и ту же маску.

Что именно делает эта линия?

Разблокирует SIGCLD у ребенка - опять же, не влияя на родителя - в случае, если его заблокировать, будет мешать поведению /bin/date, который ребенок собирается исполнить.

Другие вопросы по тегам