Книга: Операционная система UNIX
Надежные сигналы
Надежные сигналы
Стандарт POSIX. 1 определил новый набор функций управления сигналами. основанный на интерфейсе 4.2BSD UNIX и лишенный рассмотренных выше недостатков.
Модель сигналов, предложенная POSIX, основана на понятии набора сигналов (signal set), описываемого переменной типа sigset_t
. Каждый бит этой переменной отвечает за один сигнал. Во многих системах тип sigset_t
имеет длину 32 бита, ограничивая количество возможных сигналов числом 32.
Следующие функции позволяют управлять наборами сигналов:
#include <signal.h>
int sigempyset(sigset_t *set);
int siufillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(sigset_t *set, int signo);
В отличие от функции signal(3C), изменяющей диспозицию сигналов, данные функции позволяют модифицировать структуру данных sigset_t
, определенную процессом. Для управления непосредственно сигналами используются дополнительные функции, которые мы рассмотрим позже.
Функция sigemptyset(3C) инициализирует набор, очищая все биты. Если процесс вызывает sigfillset(3C), то набор будет включать все сигналы, известные системе. Функции sigaddset(3C) и sigdelset(3C) позволяют добавлять или удалять сигналы набора. Функция sigismember(3C) позволяет проверить, входит ли указанный параметром signo
сигнал в набор.
Вместо функции signal(3C) стандарт POSIX. 1 определяет функцию sigaction(2), позволяющую установить диспозицию сигналов, узнать ее текущее значение или сделать и то и другое одновременно. Функция имеет следующее определение:
#include <signal.h>
int sigaction (int sig, const struct sigaction *act,
struct sigaction *oact);
Вся необходимая для управлением сигналами информация передается через указатель на структуру sigaction
, имеющую следующие поля:
void (*sa_handler)() |
Обработчик сигнала sig |
void (*sa_sigaction)(int, siginfo_t*, void*) |
Обработчик сигнала sig при установленном флаге SA_SIGINFO |
sigset_t sa_mask |
Маска сигналов |
int sa_flags |
Флаги |
Поле sa_handler
определяет действие, которое необходимо предпринять при получении сигналов, и может принимать значения SIG_IGN
, SIG_DFL
или адреса функции-обработчика. Если значение sa_handler
или sa_sigaction
не равны NULL
, то в поле sa_mask
передается набор сигналов, которые будут добавлены к маске сигналов перед вызовом обработчика. Каждый процесс имеет установленную маску сигналов, определяющую сигналы, доставка которых должна быть заблокирована. Если определенный бит маски установлен, соответствующий ему сигнал будет заблокирован. После возврата из функции-обработчика значение маски возвращается к исходному значению. Заметим, что сигнал, для которого установлена функция-обработчик, также будет заблокирован перед ее вызовом. Такой подход гарантирует, что во время обработки, последующее поступление определенных сигналов будет приостановлено до завершения функции. Как правило, UNIX не поддерживает очередей сигналов, и это значит, что блокировка нескольких однотипных сигналов в конечном итоге вызовет доставку лишь одного.
Поле sa_flags
определяет флаги, модифицирующие доставку сигнала. Оно может принимать следующие значения:
SA_ONSTACK |
Если определена функция-обработчик сигнала, и с помощью функции sigaltstack(2) задан альтернативный стек для функции-обработчика, то при обработке сигнала будет использоваться этот стек. Если флаг не установлен, будет использоваться обычный стек процесса. |
SA_RESETHAND * |
Если определена функция-обработчик, то диспозиция сигнала будет изменена на SIG_DFL , и сигнал не будет блокироваться при запуске обработчика. Если флаг не установлен, диспозиция сигнала остается неизменной. |
SA_NODEFER * |
Если определена функция-обработчик, то сигнал блокируется на время обработки только в том случае, если он явно указан в поле sa_mask . Если флаг не установлен, в процессе обработки данный сигнал автоматически блокируется. |
SA_RESTART |
Если определена функция-обработчик, ряд системных вызовов, выполнение которых было прервано полученным сигналом, будут автоматически перезапущены после обработки сигнала.[25] Если флаг не установлен, системный вызов возвратит ошибку EINTR . |
SA_SIGINFO * |
Если диспозиция указывает на перехват сигнала, вызывается функция, адресованная полем sa_sigaction . Если флаг не установлен, вызывается обработчик sa_handler . |
SA_NOCLDWAIT * |
Если указанный аргументом sig сигнал равен SIGCHLD , при завершении потомки не будут переходить в состояние зомби. Если процесс в дальнейшем вызовет функции wait(2), wait3(2), waitid(2) или waitpid(2), их выполнение будет блокировано до завершения работы всех потомков данного процесса. |
SA_NOCLDSTOP * |
Если указанный аргументом sig сигнал равен SIGCHLD , указанный сигнал не будет отправляться процессу при завершении или останове любого из его потомков. |
*Данные флаги не определены для UNIX BSD.
В системах UNIX BSD 4.x структура sigaction
имеет следующий вид:
struct sigaction {
void (*sa_handler)();
sigset_t sa_mask;
int sa_flags;
};
где функция-обработчик определена следующим образом:
void handler(int signo, int code, struct sigcontext *scp);
В первом аргументе signo
содержится номер сигнала, code
определяет дополнительную информацию о причине поступления сигнала, a scp
указывает на контекст процесса.
Для UNIX System V реализована следующая возможность получения более полной информации о сигнале. Если установлен флаг SA_SIGINFO
, то при получении сигнала sig
будет вызван обработчик, адресованный полем sa_sigaction
. Помимо номера сигнала, обычно передаваемого обработчику сигнала, ему будет переданы указатель на структуру siginfo_t
, содержащую информацию о причинах получения сигнала, а также указатель на структуру ucontext_t
, содержащую контекст процесса.
Структура siginfo_t
определена в файле <siginfo.h> и включает следующие поля:
int si_signo |
Номер сигнала |
int si_errno |
Номер ошибки |
int si_code |
Причина отправления сигнала |
В поле si_signo
хранится номер сигнала. Поле si_code
имеет следующий смысл: если его значение меньше или равно нулю, значит сигнал был отправлен прикладным процессом, в этом случае структура siginfo_t
содержит также следующие поля:
pid_t si_pid |
Идентификатор процесса PID |
uid_t si_uid |
Идентификатор пользователя UID |
которые адресуют процесс, пославший сигнал; если значение si_code
больше нуля, то оно указывает на причину отправления сигнала. Список возможных значений si_code
для некоторых сигналов, соответствующих полю si_signo
, приведен в табл. 2.19
Таблица 2.19. Значения поля si_code структуры siginfo_t для некоторых сигналов
Значение поля si_signo |
Значение поля si_code |
Описание |
---|---|---|
SIGILL |
Попытка выполнения недопустимой инструкции | |
ILL_ILLOPC |
Недопустимый код операции (opcode) | |
ILL_ILLOPN |
Недопустимый операнд | |
ILL_ADR |
Недопустимый режим адресации | |
ILL_ILLTRP |
Недопустимая ловушка (trap) | |
ILL_PRVOPC |
Привилегированный код операции | |
ILL_PRVREG |
Привилегированный регистр | |
ILL_COPROC |
Ошибка сопроцессора | |
ILL_BADSTK |
Ошибка внутреннего стека | |
SIGFPE |
Особая ситуация операции с плавающей точкой | |
FPE_INTDIV |
Целочисленное деление на ноль | |
FPE_INTOVF |
Целочисленное переполнение | |
FPE_FLTDIV |
Деление на ноль с плавающей точкой | |
FPE_FLTOVF |
Переполнение с плавающей точкой | |
FPE_FLTUND |
Потеря точности с плавающей точкой (underflow) | |
FPE_FLTRES |
Неоднозначный результат операции с плавающей точкой | |
FPE_FLTINV |
Недопустимая операция с плавающей точкой | |
FPE_FLTSUB |
Индекс вне диапазона | |
SIGSEGV |
Нарушение сегментации | |
SEGV_MAPPER |
Адрес не отображается на объект | |
SEGV_ACCERR |
Недостаточно прав на отображаемый объект | |
SIGBUS |
Ошибка адресации | |
BUS_ADRALN |
Недопустимое выравнивание адреса | |
BUS_ADRERR |
Несуществующий физический адрес | |
BUS_OBJERR |
Аппаратная ошибка, связанная с объектом | |
SIGTRAP |
Ловушка | |
TRAP_BRKPT |
Процессом достигнута точка останова | |
TRAP_TRACE |
Ловушка трассирования процесса | |
SIGCHLD |
Завершение выполнения дочернего процесса | |
CLD_EXITED |
Дочерний процесс завершил выполнение | |
CLD_KILLED |
Дочерний процесс был "убит" | |
CLD_DUMPED |
Ненормальное завершение дочернего процесса | |
CLD_TRAPPED |
Трассируемый дочерний процесс находится в ловушке | |
CLD_STOPPED |
Выполнение дочернего процесса было остановлено | |
CLD_CONTINUED |
Выполнение остановленного дочернего процесса было продолжено | |
SIGPOLL |
Событие на опрашиваемом устройстве | |
POLL_IN |
Поступили данные для ввода | |
POLL_OUT |
Свободны буферы данных | |
POLL_MSG |
Сообщение ожидает ввода | |
POLL_ERR |
Ошибка ввода/вывода | |
POLL_PRI |
Высокоприоритетные данные ожидают ввода | |
POLL_HUP |
Устройство отключено |
Уже отмечалось, что при получении сигнала от пользовательского процесса структура siginfo_t
содержит дополнительные поля (табл. 2.20).
Таблица 2.20. Дополнительные поля структуры siginfo_t
Значение поля si_signo |
Дополнительные поля | Значение |
---|---|---|
SIGILL SIGFPE | caddr_t si_addr |
Адрес недопустимой инструкции |
SIGSEGV SIGBUS | caddr_t si_addr |
Адрес недопустимой области памяти |
SIGCHLD | pid_t si_pid |
Идентификатор дочернего процесса |
int si_status |
Код возврата сигнала | |
SIGPOLL | long si_band |
Ошибка канала (для модулей STREAMS) |
Установить маску сигналов или получить текущую маску можно с помощью функции sigprocmask(2):
#include <signal.h>
int sigprocmask(int how, sigset_t *set, sigset_t *oset);
Маска сигналов изменяется в соответствии с аргументом how
, который может принимать следующие значения:
SIG_BLOCK |
Результирующая маска получится путем объединения текущей маски и набора set |
SIG_UNBLOCK |
Сигналы набора set будут удалены из текущей маски |
SIG_SETMASK |
Текущая маска будет заменена на набор set |
Если указатель set
равен NULL
, то аргумент how игнорируется. Если аргумент oset
не равен NULL
, то в набор, адресованный этим аргументом, помещается текущая маска сигналов.
Функция sigpending(2) используется для получения набора заблокированных сигналов, ожидающих доставки:
#include <signal.h>
int sigpending(int how, sigset_t *set, sigset_t *oset);
Список сигналов, ожидающих доставки, возвращается в наборе, адресованном аргументом set
.
Системный вызов sigsuspend(2) замещает текущую маску набором, адресованным аргументом set
, и приостанавливает выполнение процесса до получения сигналов, диспозиция которых установлена либо на завершение выполнения процесса, либо на вызов функции-обработчика сигнала.
#include <signal.h>
int sigsuspend(const sigset_t *set);
При получении сигнала, завершающего выполнение процесса, возврата из функции sigsuspend(2) не происходит. Если же диспозиция полученного сигнала установлена на вызов функции-обработчика, возврат из sisuspend(2) происходит сразу после завершения обработки сигнала. При этом восстанавливается маска, существовавшая до вызова sigsuspend(2).
Заметим, что в BSD UNIX вызов signal(3) является упрощенным интерфейсом к более общей функции sigaction(2), в то время как в ветви System V signal(3) подразумевает использование старой семантики ненадежных сигналов.
В заключение для иллюстрации изложенных соображений, приведем версию функции signal(), позволяющую использовать надежные сигналы. Похожая реализация используется в BSD UNIX. С помощью этой "надежной" версии мы повторим пример, рассмотренный нами выше, в измененном виде.
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Вариант "надежной" функции signal() */
void (*mysignal(int signo, void (*hndlr)(int)))(int) {
struct sigaction act, oact;
/* Установим маску сигналов */
act.sa_handler = hndlr;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo != SIGALRM)
act.sa_flags = SA_RESTART;
/* Установим диспозицию */
if (sigaction(signo, &act, &oact) < 0)
return SIG_ERR;
return(oact.sa_handler);
}
/* Функция-обработчик сигнала */
static void sig_hndlr(int signo) {
/* Эта часть кода нам уже не нужна
mysignal(SIGINT, sig_hndlr);
*/
printf("Получен сигнал SIGINTn");
}
main() {
/* Установим диспозицию */
mysignal(SIGINT, sig_hndlr);
mysignal(SIGUSR2, SIG_IGN);
/* Бесконечный цикл */
while (1)
pause();
}
Заметим, что при использовании надежных сигналов, не нужно восстанавливать диспозицию в функции-обработчике при получении сигнала.
- Сигналы
- Надежные источники времени
- 12.1.3. Надежные сигналы
- 12.6. Сигналы реального времени
- Звуковые сигналы BIOS
- 7.2.6.3. Системные демоны и традиционные сигналы
- Сигналы и шумы
- Световые сигналы
- Глава 10 Сигналы
- 10.3. Стандартные сигналы С: signal() и raise()
- 10.6. Сигналы POSIX
- 10.7. Сигналы для межпроцессного взаимодействия