Запуск этой программы выдаст показанные ниже результаты.
Принятые сигналы:
User defined signal1
SIGRTMIN + 0
SIGRTMIN + 0
SIGRTMIN + 0
SIGRTMIN + 1
SIGRTMIN + 1
Это показывает, что все сигналы реального времени были доставлены, в то же время, был доставлен только один экземпляр сигнала SIGUSR1. Вы также видите изменение порядка сигналов реального времени — все сигналы SIGRTMIN были доставлены перед SIGRTMIN + 1.
12.7. Дополнительные сведения о сигналах
Сигналы, которые мы обсуждали до сих пор, не несли в себе никаких данных; появление сигнала — это единственная информация, которую получает приложение. В некоторых случаях было бы неплохо знать, что послужило причиной отправки сигнала (как, например, неправильная адресация памяти, генерирующая SIGSEGV), или же иметь возможность включить данные в сигналы, генерируемые приложением. Расширение реального времени Real Time Signals позволяет решить обе эти задачи.
12.7.1. Получение контекста сигнала
Информация о том, как и почему был сгенерирован сигнал, называется контекстом[68] сигнала. Приложения, которые должны видеть этот контекст, используют обработчики сигналов, отличающиеся от нормальных. Они включают два дополнительных параметра — указатель на siginfo_t, предоставляющий контекст сигнала, и указатель на void*, который может быть использован некоторыми низкоуровневыми системными библиотеками[69]. Вот как выглядит полный прототип такого обработчика.
void handler(int signum, siginfo_t *siginfo, void *context);
Приложение должно указать ядру на необходимость передачи полной информации о контексте, устанавливая флаг SA_SIGINFO члена sa_mask структуры struct sigaction, применяемой для регистрации обработчика сигнала. Член sa_handler также не используется, потому что он является указателем на функцию с другим прототипом. Вместо этого новый член, sa_sigaction, указывает на обработчик сигнала с правильным прототипом. Чтобы снизить потребление памяти, sa_handler и sa_sigaction разрешено использовать один и тот же участок памяти, поэтому только один из двух должен применяться в одно и то же время. Чтобы сделать это прозрачным, библиотека С определяет struct sigaction следующим образом.
#include <signal.h>
struct sigaction {
union {
__sighandler_t sa_handler;
__sigaction_t sa_sigaction;
} __sigaction_handler;
sigset_t sa_mask;
unsigned long sa_flags;
};
#define sa_handler __sigaction_handler.sa_handler
#define sa_sigaction __sigaction_handler.sa_sigaction
Использование представленной комбинации объединений и макросов позволяет этим двум членам разделять одну и ту же память без необходимости усложнения с точки зрения приложений.
Структура siginfo_t содержит информацию о том, где и почему был сгенерирован сигнал. Всем сигналам доступны два члена: sa_signo и si_code. Какие другие члены доступны — зависит от конкретного сигнала, и эти члены разделяют память подобно тому, как это делают члены sa_handler и sa_sigaction структуры struct sigaction. Член sa_signo содержит номер доставленного сигнала и всегда равен значению первого параметра, переданного обработчику сигнала, в то время как si_code указывает, почему сигнал был сгенерирован, и изменяется в зависимости от номера сигнала. Для большинства сигналов он может принимать перечисленные ниже значения.[70]
SI_USER
Приложение пространства пользователя вызвало kill() для отправки сигнала. Примечание. Функция sigsend(), включенная в Linux для совместимости с некоторыми системами Unix, также выдает SI_USER.
SI_QUEUE
Приложение пространства пользователя вызвало sigqueue() для от правки сигнала, что обсуждается в самом конце этой главы.
SI_TKILL
Приложение пространства пользователя вызвало tkill(). В то время как ядро Linux использует SI_TKILL, его значение не специфицировано в текущей версии библиотеки С.
Если вам нужно проверить SI_TKILL, используйте следующий сегмент кода для определения этого значения:
#ifndef SI_TKILL
#define SI_TKILL -6
#endif
SI_TKILL не специфицирован ни в каком стандарте (хотя допускается ими), поэтому его следует применять осторожно в переносимых программах.
SI_KERNEL
Сигнал сгенерирован ядром.
Когда SIGILL, SIGFPE, SIGSEGV, SIGBUS и SIGCHLD посылаются ядром, то si_code вместо si_kernel принимает значения, перечисленные в табл. 12.3[71].
Таблица 12.3. Значения si_code для специальных сигналов
| Сигнал | si_code | Описание |
|---|---|---|
SIGILL |
ILL_ILLOPC |
Неправильный код операции (opcode). |
ILL_ILLOPC |
Неправильный операнд. | |
ILL_ILLOPC |
Неправильный режим адресации. | |
ILL_ILLOPC |
Неправильная ловушка (trap). | |
ILL_ILLOPC |
Привилегированный код операции. | |
ILL_ILLOPC |
Привилегированный регистр. | |
ILL_ILLOPC |
Внутренняя ошибка стека. | |
ILL_ILLOPC |
Ошибка сопроцессора. | |
SIGFPE |
FPE_INTDIV |
Деление целого на ноль. |
FPE_INTOVF |
Переполнение целого. | |
FPE_FLTDIV |
Деление числа с плавающей точкой на ноль. | |
FPE_FLTOVF |
Переполнение числа с плавающей точкой. | |
FPE_FLTUND |
Потеря значимости числа с плавающей точкой. | |
FPE_FLTRES |
Неточный результат числа с плавающей точкой. | |
FPE_FLTINV |
Неверная операция с плавающей точкой. | |
FPE_FLTSUB |
Число с плавающей точкой вне диапазона. | |
SIGSEGV |
SEGV_MAPPER |
Адрес не отображается на объект. |
SEGV_ACCERR |
Неверные права доступа для адреса. | |
SIGBUS |
BUS_ADRALN |
Неверное выравнивание адреса. |
BUS_ADRERR |
Несуществующий физический адрес. | |
BUS_OBJERR |
Специфичный для объекта сбой оборудования. | |
SIGCHLD |
CLD_EXITED |
Дочерний процесс завершен. |
CLD_KILLED |
Дочерний процесс уничтожен. | |
CLD_DUMPED |
Дочерний процесс уничтожен с выводом дампа памяти в файл. | |
CLD_TRAPPED |
Дочерний процесс достиг точки останова. | |
CLD_STOPPED |
Дочерний процесс приостановлен. |
68
До появления стандарта POSIX приложение могло обращаться к struct sigcontext за информацией того же рода, что теперь представляет siginfo_t, и термин "контекст" остался от этой старой реализации.
69
Этот третий параметр на самом деле указывает на структуру struct ucontext, которая позволяет процессам выполнять полное переключение контекстов в пользовательском пространстве. Данные вопросы выходят за пределы тем, рассматриваемых в настоящей книге, но это хорошо документировано в Single Unix Specification.
70
Существует гораздо больше значений si_code, нежели мы обсуждаем здесь, и эти значения имеют отношение к асинхронному вводу-выводу, очередям сообщений и таймерам реального времени, что выходит за границы тем, обсуждаемых в книге.
71
Он также принимает специальное значение SIGTRAP, которое используется отладчиками, и SIGPOLL, применяемое механизмом ненадежного асинхронного ввода-вывода. Ни один из них не описан в настоящей книге, поэтому подробности об этих сигналах не включены в табл. 12.3.