Так как прототип вышеприведенного обработчика сигнала отличается от прототипа стандартного обработчика, правила типов языка С не позволяют нам задействовать поле sa_handler структуры sigaction для указания адреса этого обработчика. По этой причине мы вынуждены использовать альтернативное поле: sa_sigaction. Иными словами, определение структуры sigaction несколько более сложно, чем было показано в разделе 20.13. Полностью определение этой структуры выглядит следующим образом:
struct sigaction {
union {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
} __sigaction_handler;
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
/* Благодаря следующим строкам define поля union выглядят
как простые поля родительской структуры */
#define sa_handler __sigaction_handler.sa_handler
#define sa_sigaction __sigaction_handler.sa_sigaction
В структуре sigaction используется объединение для сочленения полей sa_sigaction и sa_handler. (В большинстве реализаций UNIX объединение применяется для аналогичных целей.) Объединение возможно, потому что при осуществлении каждого конкретного вызова функции sigaction() требуется только одно из этих полей. (Попытка независимо друг от друга установить значения полей sa_sigaction и sa_handler может привести к ошибкам, возможно, из-за того, что мы повторно используем одну и ту же структуру sigaction для нескольких вызовов функции sigaction(), чтобы установить обработчики различных сигналов.)
Далее приведен пример задействования флага SA_SIGINFO для установки одного обработчика:
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGINT, &act, NULL) == –1)
errExit("sigaction");
Для ознакомления с завершенными примерами использования флага SA_SIGINFO см. листинги 22.3 и 23.5.
Структура siginfo_t, которая передается в качестве второго аргумента обработчику сигнала, установленного с помощью флага SA_SIGINFO, имеет такой вид:
typedef struct {
int si_signo; /* Номер сигнала */
int si_code; /* Код сигнала */
int si_trapno; /* Номер ловушки для аппаратно генерируемого сигнала
(не используется в большинстве архитектур) */
union sigval si_value; /* Дополнительные данные из sigqueue() */
pid_t si_pid; /* ID посылающего процесса */
uid_t si_uid; /* Реальный ID пользователя
посылающего процесса */
int si_errno; /* Номер ошибки (обычно не используется) */
void *si_addr; /* Адрес, сгенерировавший сигнал (только
для аппаратно-генерируемых сигналов) */
int si_overrun; /* Счетчик переполнений таймера
(Linux 2.6, таймеры POSIX) */
int si_timerid; /* (Внутриядерный) ID таймера
(Linux 2.6, таймеры POSIX) */
long si_band; /* Связывающее событие (SIGPOLL/SIGIO) */
int si_fd; /* Файловый дескриптор (SIGPOLL/SIGIO) */
int si_status; /* Код завершения или сигнал (SIGCHLD) */
clock_t si_utime; /* Пользовательское время ЦП (SIGCHLD) */
clock_t si_stime; /* Системное время ЦП (SIGCHLD) */
} siginfo_t;
Макрос тестирования возможности _POSIX_C_SOURCE должен быть определен со значением, большим или равным 199309, чтобы объявление структуры siginfo_t было доступно из файла
В Linux, как и в большинстве реализаций UNIX, многие поля структуры siginfo_t объединены, так как не все поля нужны для каждого сигнала. (См.
На входе в обработчик сигнала поля структуры siginfo_t установлены следующим образом.
• si_signo — устанавливается для всех сигналов. Поле содержит номер сигнала, вызвавшего активацию обработчика, то есть то же самое значение, что и аргумент sig обработчика.
• si_code — устанавливается для всех сигналов. Содержит код, предоставляющий дополнительную информацию об источнике сигнала, как показано в табл. 21.2.
• si_value — содержит дополнительные данные, отправляемые в сигнал с помощью функции sigqueue(). Функция sigqueue() описывается в подразделе 22.8.1.
• si_pid — для сигналов, отправляемых через функцию kill() или siqueue(), это поле содержит идентификатор пославшего сигнал процесса.
• si_uid — для сигналов, отправляемых через функцию kill() или siqueue(), это поле содержит реальный ID пользователя процесса, пославшего сигнал. Система предоставляет реальный ID пользователя процесса, так как он является более информативным по сравнению с действующим ID пользователя. Рассмотрим права доступа для отправки сигналов, описанные в разделе 20.5: если действующий ID пользователя дает отправителю право послать сигнал, значит, он должно быть равен 0 (то есть привилегированный процесс) или совпадать с реальным или сохраненным установленным ID пользователя процесса, получающего сигнал. В этом случае получателю было бы полезно знать реальный ID пользователя отправителя, которое может отличаться от действующего ID пользователя (если отправитель — это программа с установленным ID пользователя).