16:45:18 handler: Caught SIGCHLD
16:45:18 handler: Reaped child 17767 — child exited, status=0
16:45:19 Child 2 (PID=17768) exiting
16:45:21 Child 3 (PID=17769) exiting
16:45:23 handler: returning
16:45:23 handler: Caught SIGCHLD
16:45:23 handler: Reaped child 17768 — child exited, status=0
16:45:23 handler: Reaped child 17769 — child exited, status=0
16:45:28 handler: returning
16:45:28 All 3 children have terminated; SIGCHLD was caught 2 times
Обратите внимание на вызов sigprocmask() в листинге 26.5
Листинг 26.5. Снятие завершенных дочерних процессов с помощью обработчика SIGCHLD
procexec/multi_SIGCHLD.c
#include
#include
#include "print_wait_status.h"
#include "curr_time.h"
#include "tlpi_hdr.h"
static volatile int numLiveChildren = 0;
/* Количество запущенных потомков, которых еще не дождались */
static void
sigchldHandler(int sig)
{
int status, savedErrno;
pid_t childPid;
/* НЕБЕЗОПАСНО: этот обработчик использует функции printf(), printWaitStatus(),
currTime(), небезопасные в контексте асинхронных сигналов; (подраздел 21.1.2) */
savedErrno = errno; /* На случай, если мы изменим значение 'errno' */
printf("%s handler: Caught SIGCHLD\n", currTime("%T"));
while ((childPid = waitpid(–1, &status, WNOHANG)) > 0) {
currTime("%T"), (long) childPid);
printWaitStatus(NULL, status);
numLiveChildren-;
}
if (childPid == –1 && errno!= ECHILD)
errMsg("waitpid");
printf("%s handler: returning\n", currTime("%T"));
errno = savedErrno;
}
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); /* Отключаем буферизацию стандартного вывода */
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");
/* Блокируем сигнал SIGCHLD, чтобы предотвратить его доставку в случае, если
потомок завершится до начала цикла sigsuspend() в родителе (см. ниже) */
sigemptyset(&blockMask);
sigaddset(&blockMask, SIGCHLD);
errExit("sigprocmask");
switch (fork()) {
case –1:
errExit("fork");
case 0: /* Потомок приостанавливается, и затем завершается */
printf("%s Child %d (PID=%ld) exiting\n",
currTime("%T"), j, (long) getpid());
_exit(EXIT_SUCCESS);
default: /* Родитель входит в цикл для создания следующего потомка */
break;
}
}
/* Сюда доходит родитель: ждем сигнала SIGCHLD, пока не завершатся все потомки */
sigemptyset(&emptyMask);
while (numLiveChildren > 0) {
errExit("sigsuspend");
sigCnt++;
}
printf("%s All %d children have terminated; SIGCHLD was caught "
"%d times\n", currTime("%T"), argc — 1, sigCnt);
exit(EXIT_SUCCESS);
}
procexec/multi_SIGCHLD.c
26.3.2. Доставка сигнала SIGCHLD для остановленных потомков
По аналогии с тем, как вызов waitpid() может быть использован для отслеживания остановленных потомков, родитель может получать сигнал SIGCHLD в случае остановки одного из его дочерних процессов (тоже по сигналу). Этого можно добиться с помощью флага SA_NOCLDSTOP, когда вызов sigaction() устанавливает обработчик SIGCHLD. Если этот флаг опущен, сигнал SIGCHLD доставляется родителю при остановке одного из потомков; если флаг присутствует, этого не происходит (реализация вызова signal(), показанная в разделе 22.7, не предусматривает флага SA_NOCLDSTOP).
Поскольку сигнал SIGCHLD по умолчанию игнорируется, флаг SA_NOCLDSTOP имеет значение только в том случае, если установить обработчик SIGCHLD. Более того, SIGCHLD — это единственный сигнал, на который влияет флаг SA_NOCLDSTOP.