Этот сигнал вновь запускает остановленный процесс. Если процесс не остановлен, он игнорируется. При желании его можно перехватить, но опять-таки для большинства программ мало причин для осуществления этого. Продолжая наш пример, обработчик SIGCONT
для экранного редактора должен перед возвращением вернуть терминал обратно в посимвольный режим.
Когда процесс остановлен, любые другие посланные ему сигналы становятся ожидающими. Исключением является SIGKILL
, который всегда доставляется процессу и который не может быть перехвачен, заблокирован или проигнорирован. В предположении, что были посланы сигналы кроме SIGKILL
, по получении SIGCONT
ожидающие сигналы доставляются, а процесс продолжает выполнение после того, как они будут обработаны.
10.8.3. Родительский надзор: три различные стратегии
Как описано в разделе 9.1.1 «Создание процесса: fork()
», одним побочным эффектом вызова fork()
является создание между процессами отношений родитель-потомок. Родительский процесс может ждать завершения одного или более из своих потомков и получить статус завершения порожденного процесса посредством одного из семейства системных вызовов wait()
.
Завершившиеся порожденные процессы, которых никто не ожидал, называются SIGCHLD
[112]. Действием по умолчанию является игнорирование этого сигнала. В этом случае процессы зомби накапливаются до тех пор, пока родитель не вызовет wait()
или не закончится сам. В последнем случае процессы зомби получают в качестве нового родителя системный процесс init
(PID 1), который получает от них результаты как часть своей обычной работы. Сходным образом, активные потомки также получают родителем init
, и их результаты будут собраны при их завершении.
SIGCHLD
используется для большего, чем уведомление о завершении потомка. Каждый раз при остановке потомка (посредством одного из обсужденных ранее сигналов управления заданиями) родителю также посылается SIGCHLD
. Стандарт POSIX указывает, что SIGCHLD
«может быть послан» также, когда помок вновь запускается; очевидно, среди оригинальных Unix-систем имеются различия.
Сочетание флагов для поля sa_flags
в struct sigation
и использование SIG_IGN
в качестве действия для SIGCHLD
позволяет изменить способ обработки ядром остановок, возобновления или завершения потомков.
Как и с сигналами в общем, описанные здесь интерфейсы и механизмы сложны, поскольку они развивались с течением времени.
10.8.3.1. Плохие родители: полное игнорирование потомков
Простейшим действием, которое вы можете сделать, является изменение действия для SIGCHLD
на SIG_IGN
. В этом случае завершившиеся потомки не становятся зомби. Вместо этого статус их завершения отбрасывается, и они полностью удаляются из системы.
Другой возможностью, дающей такой же результат, является использование флага SA_NOCLDWAIТ
. В коде:
/* Старый стиль: */ /* Новый стиль: */
signal(SIGCHLD, SIG_IGN); struct sigaction sa;
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_NOCLDWAIT;
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
10.8.3.2. Снисходительные родители: минимальный надзор
В качестве альтернативы можно беспокоиться лишь о завершении потомка и не интересоваться простыми изменениями состояния (остановке и возобновлении). В этом случае используйте флаг SA_NOCLDSTOP
и установите обработчик сигнала, вызывающий wait()
(или родственную ей функцию) для получения данных процесса.
В общем вы не можете ожидать получать по одному сигналу SIGCHLD
на каждого завершающегося потомка. Следует считать, что SIGCHLD
означает «завершился по крайней мере один потомок» и быть готовым собрать при обработке SIGCHLD
сведения о как можно большем числе потомков.
Следующая программа, ch10-reap1.с
, блокирует SIGCHLD
до тех пор, пока не будет готова восстановить потомков.
1 /* ch10-reap1.с --- демонстрирует управление SIGCHLD с использованием цикла */
2
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9
10 #define MAX_KIDS 42
11 #define NOT_USED -1
12
13 pid_t kids[MAX_KIDS];
14 size_t nkids = 0;