Книга: Разработка приложений в среде Linux. Второе издание
12.7.1. Получение контекста сигнала
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 |
Дочерний процесс приостановлен. |
Чтобы помочь прояснить разные значения, которые может принимать si_code
, рассмотрим пример, в котором SIGCHLD
генерируется четырьмя разными способами: kill()
, sigqueue()
, raise
() (использует системный вызов tkill()
) и созданием дочернего процесса, который немедленно прерывается.
1: /* sicode.с */
2:
3: #include <sys/signal.h>
4: #include <stdlib.h>
5: #include <stdio.h>
6: #include <unistd.h>
7:
8: #ifndef SI_TKILL
9: #define SI_TKILL -6
10: #endif
11:
12: void handler(int signo, siginfo_t *info, void *f ) {
13: static int count = 0;
14:
15: printf("перехвачен сигнал, отправленный ");
16: switch(info->si_code) {
17: case SI_USER:
18: printf("kill()n"); break;
19: case SI_QUEUE:
20: printf("sigqueue()n"); break;
21: case SI_TKILL:
22: printf("tkill() или raise()n"); break;
23: case CLD_EXITED:
24: printf ("ядро сообщает, что дочерний процесс завершенn"); exit(0);
25: }
26:
27: if (++count == 4) exit(1);
28: }
29:
30: int main() {
31: struct sigaction act;
32: union sigval val;
33: pid_t pid = getpid();
34:
35: val.sival_int = 1234;
36:
37: act.sa_sigaction = handler;
38: sigemptyset(&act.sa_mask);
39: act.sa_flags = SA_SIGINFO;
40: sigaction(SIGCHLD, &act, NULL);
41:
42: kill(pid, SIGCHLD);
43: sigqueue(pid, SIGCHLD, val);
44: raise(SIGCHLD);
45:
46: /* Чтобы получить SIGCHLD от ядра, мы создаем дочерний процесс
47: и немедленно завершаем его. Обработчик сигнала выйдет после
48: получения сигнала от ядра, поэтому мы просто засыпаем
49: на время и позволяем программе прерваться подобным образом. */
50:
51: if (!fork()) exit(0);
52: sleep(60);
53:
54: return 0;
55: }
Если si_code
равно SI_USER
, SI_QUEUE
или SI_TKILL
, то доступны два дополнительных члена siginfo_t
: si_pid
и si_uid
, которые представляют идентификатор процесса, пославшего сигнал и действительный идентификатор пользователя этого процесса.
Когда ядром посылается SIGCHLD
, доступны члены si_pid
, si_status
, si_utime
и si_stime
. Первый из них, si_pid
, задает идентификатор процесса, состояние которого изменилось[72]. Информация о новом состоянии доступна как в si_code
(как показано в табл. 12.3) и в si_status
, что идентично целому значению состояния, возвращаемому семейством функций wait()
.
Последние два члена, si_utime
и si_stime
, определяют период времени, которое потрачено дочерним приложением на работу в пользовательском режиме и в режиме ядра, соответственно (это подобно тому, что возвращают вызовы wait3()
и wait4()
в структуре struct rusage
). Это время измеряется в тиках часов, заданных целым числом. Количество тиков в секунду задает макрос _SC_CLK_TCK
, определенный в <sysconf.h>
.
SIGSEGV
, SIGBUS
, SIGILL
и SIGFPE
— все они представляют si_addr
, специфицирующий адрес, который вызвал сбой, описанный si code
.
Ниже приведен простой пример проверки контекста сигнала. Он устанавливает обработчик сигнала для SIGSEGV
, который печатает контекст сигнала и прерывает процесс. Нарушение сегментации генерируется попыткой обращения к NULL
.
1: /* catch-segv.c */
2:
3: #include <sys/signal.h>
4: #include <stdlib.h>
5: #include <stdio.h>
6:
7: void handler(int signo, siginfo_t *info, void *f) {
8: printf("перехват");
9: if (info->si_signo == SIGSEGV)
10: printf("segv accessing %p", info->si_addr);
11: if (info->si_code == SEGV_MAPERR)
12: printf("SEGV_MAPERR");
13: printf("n");
14:
15: exit(1);
16: }
17:
18: int main() {
19: struct sigactin act;
20:
21: act.sa_sigaction = handler;
22: sigemptyset(&act.sa_mask);
23: act.sa_flags = SA_SIGINFO;
24: sigaction(SIGSEGV, &act, NULL);
25:
26: *((int *)NULL) = 1 ;
27:
28: return 0;
29: }
- 12.7. Дополнительные сведения о сигналах
- Получение статистики
- Получение помощи
- Получение помощи по работе с книгой и компакт-диском
- Получение помощи по Windows SharePoint Services 3.0
- Получение доменного имени
- 4.8. Получение прав root
- Получение страниц заполненных нулями
- Получение статистики браузера
- Режимы: форматирование в зависимости от контекста
- Получение, создание и отправка электронной корреспонденции
- 4.23.3. Запуск измерительной последовательности от внешнего сигнала