Почему обработчик sigaction не обрабатывает каждый сигнал, даже если установлен флаг SA_NODEFER?

У меня есть родительский процесс, который порождает десять дочерних процессов и использует обработчик SIGCHILD для уведомления о смерти дочернего процесса. Детские процессы будут уведомлять, когда они начнут, и затем выйдут немедленно.
Я использую флаг SA_NODEFER, чтобы не допустить, чтобы сигналы SIGCHLD были отброшены, когда слишком много пришло. Все дети были правильно завершены и извлечены, но в этом скрипте, а также при одновременном завершении двух сигналов через консольные сигналы отправляют на тот же время всегда отображается как одно.

#define _POSIX_C_SOURCE 200809L #include  #include  #include  #include  #include  #include  void CHLDHandler(int sig) { char child[] = "Child finished!\n"; write(1, &child, sizeof(child)); } int main(int argc, char const *argv[]) { struct sigaction sa; sa.sa_handler = CHLDHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NODEFER; sigaction(SIGCHLD,&sa,NULL); for (size_t i = 0; i < 10; i++) { int pid = fork(); if (pid == 0) { int pid = getpid(); printf("I'm a child with pid %d!\n", pid); return 0; } } while(1) { wait(NULL); } return 0; } 

Основные сигналы UNIX не стоят в очереди – только один может быть отложен (для данного streamа) за раз.

Если два дочерних процесса завершаются эффективно в одно и то же время и, таким образом, доставляют SIGCHLD в одно и то же время, ваш процесс будет иметь только один сигнал.

wait в цикле после получения SIGCHLD является давно установленным методом, чтобы компенсировать тот факт, что SIGCHLD может быть «потерян». Переместите объявление «Ребенок закончил! \ N» в цикл, который вызывает wait , и вы получите точное количество.

ОБНОВИТЬ

Если вы должны пожать в свой обработчик, вы можете вызвать wait внутри обработчика, так как он безопасен для асинхронного сигнала :

 static void CHLDHandler(int sig) { static char child[] = "Child finished!\n"; int save_errno = errno; while (waitpid((pid_t)-1, NULL, WNOHANG) > 0) { write(STDERR_FILENO, &child, sizeof(child) - 1); // don't write the terminating NULL } errno = save_errno; } 

В этом случае я бы не установил SA_NODEFER, поскольку другой SIGCHLD мог прервать (EINTR) waitpid или вызовы write .