Для каждого сигнала существует определенное действие( 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
указывая тем самым, что обработчики сигналов — это функции с целочисленным аргументом, ничего не возвращающие (
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
. Если этот флаг задан, мы устанавливаем его при перехвате сигнала