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 , который блокирует сигнал SIGCHLD до создания потомков. Это делается для того, чтобы обеспечить корректную работу цикла sigsuspend() внутри родителя. Если бы мы этого не сделали, а потомок завершился бы между проверкой значения numLiveChildren и выполнением вызова sigsuspend() (или как вариант вызова pause()), тогда вызов sigsuspend() заблокировался бы навсегда в ожидании сигнала, который уже был перехвачен . Подробности о том, как решается проблема с этим видом состояния гонки, приводятся в разделе 22.9.

Листинг 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) {

 printf("%s handler: Reaped child %ld — ",

currTime("%T"), (long) childPid);

printWaitStatus(NULL, status);

numLiveChildren-;

}

if (childPid == –1 && errno!= ECHILD)

errMsg("waitpid");

sleep(5); /* Искусственно увеличиваем время выполнения обработчика */

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);

if (sigprocmask(SIG_SETMASK, &blockMask, NULL) == –1)

errExit("sigprocmask");

for (j = 1; j < argc; j++) {

switch (fork()) {

case –1:

errExit("fork");

case 0: /* Потомок приостанавливается, и затем завершается */

 sleep(getInt(argv[j], GN_NONNEG, "child-sleep-time"));

printf("%s Child %d (PID=%ld) exiting\n",

currTime("%T"), j, (long) getpid());

_exit(EXIT_SUCCESS);

default: /* Родитель входит в цикл для создания следующего потомка */

break;

}

}

/* Сюда доходит родитель: ждем сигнала SIGCHLD, пока не завершатся все потомки */

sigemptyset(&emptyMask);

while (numLiveChildren > 0) {

 if (sigsuspend(&emptyMask) == –1 && errno!= EINTR)

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.

Перейти на страницу:

Похожие книги