Эта программа посылает себе некоторое количество сигналов и выводит на дисплей порядок их получения. Когда сигналы отправляются, она блокирует их, чтобы предотвратить немедленную доставку. Также она блокирует сигналы всякий раз, когда вызывается обработчик, устанавливая значение члена sa_mask
структуры struct sigaction
при настройке обработчика для каждого сигнала. Это предотвращает возможное состояние состязаний при обращении к глобальным переменным nextSig
и sigOrder
изнутри обработчика.
Запуск этой программы выдаст показанные ниже результаты.
Принятые сигналы:
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
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
, его значение не специфицировано в текущей версии библиотеки С.