Сигнал SIGCHLD, упомянутый в конце предыдущего раздела, ядро посылает родительскому процессу при завершении дочернего.
Для каждого сигнала существует определенное действие (action или disposition — характер). Действие, соответствующее сигналу, задается с помощью вызова функции sigaction (ее описание следует далее) и может быть выбрано тремя способами:
1. Мы можем предоставить функцию, которая вызывается при перехвате определенного сигнала. Эта функция называется обработчиком сигнала (signal handler), а действие называется перехватыванием сигнала (catching). Сигналы SIGKILL и SIGSTOP перехватить нельзя. Наша функция вызывается с одним целочисленным аргументом, который является номером сигнала, и ничего не возвращает. Следовательно, прототип этой функции имеет вид:
void handler(int signo);
Для большинства сигналов вызов функции sigaction и задание функции, вызываемой при получении сигнала, — это все, что требуется для обработки сигнала. Но дальше вы увидите, что для перехватывания некоторых сигналов, в частности SIGIO, SIGPOLL и SIGURG, требуются дополнительные действия со стороны процесса.
2. Мы можем игнорировать сигнал, если действие задать как SIG_IGN. Сигналы SIGKILL и SIGSTOP не могут быть проигнорированы.
3. Мы можем установить действие для сигнала по умолчанию, задав его как SIG_DFL. Действие сигнала по умолчанию обычно заключается в завершении процесса по получении сигнала, а некоторые сигналы генерируют копию области памяти процесса в его текущем каталоге (так называемый дамп — core dump). Есть несколько сигналов, для которых действием по умолчанию является игнорирование. Например, SIGCHLD и SIGURG (посылается по получении внеполосных данных, см. главу 24) — это два сигнала, игнорируемых по умолчанию, с которыми мы встретимся в тексте.
Функция signal
Согласно POSIX, чтобы определить действие для сигнала, нужно вызвать функцию sigaction. Однако это достаточно сложно, поскольку один аргумент этой функции — это структура, для которой необходимо выделение памяти и заполнение. Поэтому проще задать действие сигнала с помощью функции signal. Первый ее аргумент — это имя сигнала, а второй — либо указатель на функцию, либо одна из констант SIG_IGN и SIG_DFL. Но функция signal существовала еще до появления POSIX.1, и ее различные реализации имеют разную семантику сигналов с целью обеспечения обратной совместимости. В то же время POSIX четко диктует семантику при вызове функции sigaction. Это обеспечивает простой интерфейс с соблюдением семантики POSIX. Мы включили эту функцию в нашу собственную библиотеку вместе функциями err_XXX и функциями-обертками, которые мы используем для построения всех наших программ. Она представлена в листинге 5.5. Функция-обертка Signal здесь не показана, потому что ее вид не зависит от того, какую именно функцию signal она должна вызывать.
Листинг 5.5. Функция signal, вызывающая функцию POSIX sigaction
//lib/signal.c
1 #include "unp.h"
2 Sigfunc*
3 signal(int signo, Sigfunc *func)
4 {
5 struct sigaction act, oact;
6 act.sa_handler = func;
7 sigemptyset(&act.sa_mask);
8 act.sa_flags = 0;
9 if (signo == SIGALRM) {
10 #ifdef SA_INTERRUPT
11 act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
12 #endif
13 } else {
14 #ifdef SA_RESTART
15 act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
16 #endif
17 }
18 if (sigaction(signo, &act, &oact) < 0)
19 return (SIG_ERR);
20 return (oact.sa_handler);
21 }
2-3 Обычный прототип для функции signal усложняется наличием вложенных скобок:
void (*signal(int signo, void (*func)(int)))(int);
Чтобы упростить эту запись, мы определяем тип Sigfunc в нашем заголовочном файле unp.h следующим образом:
typedef void Sigfunc(int);
указывая тем самым, что обработчики сигналов — это функции с целочисленным аргументом, ничего не возвращающие (void). Тогда прототип функции выглядит следующим образом:
Sigfunc *signal(int signo, Sigfunc *func);
Указатель на функцию, являющуюся обработчиком сигнала, — это второй аргумент функции и в то же время возвращаемое функцией значение.
6 Элемент sa_handler структуры sigaction устанавливается равным аргументу func функции signal.
7 POSIX позволяет нам задавать набор сигналов, которые будут блокированы при вызове обработчика сигналов. Любой блокируемый сигнал не может быть доставлен процессу. Мы устанавливаем элемент sa_mask равным пустому набору. Это означает, что во время работы обработчика дополнительные сигналы не блокируются. POSIX гарантирует, что перехватываемый сигнал всегда блокирован, пока выполняется его обработчик.
8-17 Флаг SA_RESTART не является обязательным, и если он установлен, то системный вызов, прерываемый этим сигналом, будет автоматически снова выполнен ядром. (В продолжении нашего примера мы более подробно поговорим о прерванных системных вызовах.) Если перехватываемый сигнал не является сигналом SIGALRM, мы задаем флаг SA_RESTART, если таковой определен. (Причина, по которой сигнал SIGALRM обрабатывается отдельно, состоит в том, что обычно цель его генерации - ввести ограничение по времени в операцию ввода-вывода, как показано в листинге 14.2. В этом случае мы хотим, чтобы блокированный системный вызов был прерван сигналом.) Более ранние системы, особенно SunOS 4.x, автоматически перезапускают прерванный системный вызов по умолчанию и затем определяют флаг SA_INTERRUPT. Если этот флаг задан, мы устанавливаем его при перехвате сигнала SIGALRM.
18-20 Мы вызываем функцию sigaction, а затем возвращаем старое действие сигнала как результат функции signal.
В книге мы везде используем функцию signal из листинга 5.5.
Семантика сигналов POSIX
Сведем воедино следующие моменты, относящиеся к обработке сигналов в системе, совместимой с POSIX.
■ Однажды установленный обработчик сигналов остается установленным (в более ранних системах обработчик сигналов удалялся каждый раз по выполнении).
■ На время выполнения функции — обработчика сигнала доставляемый сигнал блокируется. Более того, любые дополнительные сигналы, заданные в наборе сигналов sa_mask, переданном функции sigaction при установке обработчика, также блокируются. В листинге 5.5 мы устанавливаем sa_mask равным пустому набору, что означает, что никакие сигналы, кроме перехватываемого, не блокируются.