указывая тем самым, что обработчики сигналов — это функции с целочисленным аргументом, ничего не возвращающие (
void
). Тогда прототип функции выглядит следующим образом:
Sigfunc *signal(int signo, Sigfunc * func);
Указатель на функцию, являющуюся обработчиком сигнала, — это второй аргумент функции и в то же время возвращаемое функцией значение.
Установка обработчика
6
Элемент
sa_handler
структуры
sigaction
устанавливается
равным аргументу
func
функции
signal
.
Установка маски сигнала для обработчика
7
POSIX позволяет нам задавать набор сигналов, которые будут блокированыпри вызове обработчика сигналов. Любой блокируемый сигнал не может быть доставлен процессу. Мы устанавливаем элемент
sa_mask
равным пустому набору. Это означает, что во время работы обработчика дополнительные сигналы не блокируются. POSIX гарантирует, что перехватываемый сигнал всегда блокирован, пока выполняется его обработчик.
Установка флага SA_RESTART
8-17
Флаг
SA_RESTART
не является обязательным, и если он установлен, то системный вызов, прерываемый этим сигналом, будет автоматически снова выполнен ядром. (В продолжении нашего примера мы более подробно поговорим о прерванных системных вызовах.) Если перехватываемый сигнал не является сигналом
SIGALRM
, мы задаем флаг
SA_RESTART
, если таковой определен. (Причина, по которой сигнал
SIGALRM
обрабатывается отдельно, состоит в том, что обычно цель его генерации - ввести ограничение по времени в операцию ввода-вывода, как показано в листинге 14.2. В этом случае мы хотим, чтобы блокированный системный вызов был прерван сигналом.) Более ранние системы, особенно SunOS 4.x, автоматически перезапускают прерванный системный вызов по умолчанию и затем определяют флаг
SA_INTERRUPT
. Если этот флаг задан, мы устанавливаем его при перехвате сигнала
SIGALRM
.
Вызов функции sigaction
18-20
Мы вызываем функцию
sigaction
, а затем возвращаем старое действие сигнала как результат функции
signal
.
В книге мы везде используем функцию
signal
из листинга 5.5.
Семантика сигналов POSIX
Сведем воедино следующие моменты, относящиеся к обработке сигналов в системе, совместимой с POSIX.
Однажды установленный обработчик сигналов остается установленным (в более ранних системах обработчик сигналов удалялся каждый раз по выполнении).
На время выполнения функции — обработчика сигнала доставляемый сигнал блокируется. Более того, любые дополнительные сигналы, заданные в наборе сигналов
sa_mask
, переданном функции
sigaction
при установке обработчика, также блокируются. В листинге 5.5 мы устанавливаем
sa_mask
равным пустому набору, что означает, что никакие сигналы, кроме перехватываемого, не блокируются.
Если сигнал генерируется один или несколько раз, пока он блокирован, то обычно после разблокирования он доставляется только один раз, то есть по умолчанию сигналы Unix не устанавливаются в очередь. Пример мы рассмотрим в следующем разделе. Стандарт POSIX реального времени 1003.1b определяет набор надежныхсигналов, которые помещаются в очередь, но в этой книге мы их не используем.
Существует возможность выборочного блокирования и разблокирования набора сигналов с помощью функции
sigprocmask
. Это позволяет нам защитить критическую область кода, не допуская перехватывания определенных сигналов во время ее выполнения.
5.9. Обработка сигнала SIGCHLD
Назначение состояния зомби — сохранить информацию о дочернем процессе, чтобы родительский процесс мог ее впоследствии получить. Эта информация включает идентификатор дочернего процесса, статус завершения и данные об использовании ресурсов (время процессора, память и т.д.). Если у завершающегося процесса есть дочерний процесс в зомбированном состоянии,
идентификатору родительского процесса всех зомбированных дочерних процессов присваивается значение 1 (процесс
init
), что позволяет унаследовать дочерние процессы и сбросить их (то есть процесс
init
будет ждать (
wait
) их завершения, благодаря чему будут удалены зомби). Некоторые системы Unix в столбце
COMMAND
выводят для зомбированных процессов значение
<defunct>
.
Обработка зомбированных процессов
Очевидно, что нам не хотелось бы оставлять процессы в виде зомби. Они занимают место в ядре, и в конце концов у нас может не остаться идентификаторов для нормальных процессов. Когда мы выполняем функцию
fork
для дочерних процессов, необходимо с помощью функции
wait
дождаться их завершения, чтобы они не превратились в зомби. Для этого мы устанавливаем обработчик сигналов для перехватывания сигнала
SIGCHLD
и внутри обработчика вызываем функцию
wait
. (Функции
wait
и
waitpid
мы опишем в разделе 5.10.) Обработчик сигналов мы устанавливаем с помощью вызова функции
Signal(SIGCHLD, sig_chld);
в листинге 5.1, после вызова функции
listen
. (Необходимо сделать это до вызова функции
fork
для первого дочернего процесса, причем только один раз.) Затем мы определяем обработчик сигнала — функцию
sig_chld
, представленную в листинге 5.6.
Листинг 5.6. Версия обработчика сигнала SIGCHLD, вызывающая функцию wait (усовершенствованная версия находится в листинге 5.8)
//tcpcliserv/sigchldwait.с
1 #include "unp.h"
2 void
3 sig_chld(int signo)
4 {
5 pid_t pid;
6 int stat;
7 pid = wait(&stat);
8 printf("child terrmnated\n", pid);
9 return;
10 }
ВНИМАНИЕ
В обработчике сигналов не рекомендуется вызов стандартных функций ввода-вывода, таких как printf, по причинам, изложенным в разделе 11.18. В данном случае мы вызываем функцию printf как средство диагностики, чтобы увидеть, когда завершается дочерний процесс.
В системах System V и Unix 98 дочерний процесс не становится зомби, если процесс задает действие SIG_IGN для SIGCHLD. К сожалению, это верно только для System V и Unix 98. В POSIX прямо сказано, что такое поведение этим стандартом не предусмотрено. Переносимый способ обработки зомби состоит в том, чтобы перехватывать сигнал SIGCHLD и вызывать функцию wait или waitpid.
Если мы откомпилируем в Solaris 9 программу, представленную в листинге 5.1, вызывая функцию
Signal
с нашим обработчиком
sig_chld
, и будем использовать функцию
signal
из системной библиотеки (вместо нашей версии, показанной в листинге 5.5), то получим следующее:
solaris % tcpserv02 &запускаем сервер в фоновом режиме
[2] 16939
solaris % tcpcli01 127.0.0.1затем клиент
hi there набираем эту строку
hi there и она отражается сервером
^D вводим символ конца файла
child 16942 terminated функция printf из обработчика сигнала выводит эту строку
accept error: Interrupted system call но функция main преждевременно прекращает выполнение