Я пытаюсь понять, как работают сигналы блокировки и разблокировки, и я пытаюсь понять следующий фрагмент кода. В частности, я смотрю на строку 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
printf()
в обработчике сигнала. Согласно 7.1.4 Использование библиотечных функций , параграф 4 стандарта C: Реентерабельность функций в стандартной библиотеке не гарантируется, и они могут изменять объекты со статической продолжительностью хранения или хранения потоков. и сноска 188: Таким образом, обработчик сигнала, как правило, не может вызывать стандартные библиотечные функции . POSIX позволяет вызывать функции, безопасные для асинхронных сигналов, из обработчика сигналов и предоставляет список.printf()
в этом списке нет. - person Andrew Henle   schedule 24.04.2019waitpid()
в обработчике сигнала, например, сwaitpid(-1, NULL, 0)
, также является плохой идеей.while ((pid = waitpid(-1, NULL, WNOHANG)) > 0);
намного лучше - он будет пожинать все дочерние процессы, которые завершились, без зависания на неопределенный срок в ожидании любого дочернего процесса, который не вышел. Если есть дочерние процессы, которые не завершились, ваш обработчик сигналов будет зависать до тех пор, пока они все не завершатся. - person Andrew Henle   schedule 25.04.2019