Должны ли дочерние процессы также разблокировать заблокированные сигналы SIGCHLD? - PullRequest
1 голос
/ 24 апреля 2019

Я пытаюсь понять, как работают сигналы блокировки и разблокировки, и я пытаюсь понять следующий фрагмент кода.В частности, я смотрю на строку 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 Ответ

1 голос
/ 25 апреля 2019

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

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

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

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

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

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

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

Что именно делает эта строка?

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

...