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() "); break;

19:  case SI_QUEUE:

20:   printf("sigqueue() "); break;

21:  case SI_TKILL:

22:   printf("tkill() или raise() "); break;

23:  case CLD_EXITED:

24:   printf ("ядро сообщает, что дочерний процесс завершен "); 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(" ");

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: }

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

6.4 СОХРАНЕНИЕ КОНТЕКСТА ПРОЦЕССА

Из книги Архитектура операционной системы UNIX автора Бах Морис Дж

6.4 СОХРАНЕНИЕ КОНТЕКСТА ПРОЦЕССА Как уже говорилось ранее, ядро сохраняет контекст процесса, помещая в стек новый контекстный уровень. В частности, это имеет место, когда система получает прерывание, когда процесс вызывает системную функцию или когда ядро выполняет


6.4.3 Переключение контекста

Из книги Программирование в X Window средствами Free Pascal автора Полищук А П

6.4.3 Переключение контекста Если обратиться к диаграмме состояний процесса (Рисунок 6.1), можно увидеть, что ядро разрешает производить переключение контекста в четырех случаях: когда процесс приостанавливает свое выполнение, когда он завершается, когда он возвращается


6.4.4 Сохранение контекста на случай аварийного завершения

Из книги Компьютер + TV: телевидение на ПК автора Гольцман Виктор Иосифович

6.4.4 Сохранение контекста на случай аварийного завершения Существуют ситуации, когда ядро вынуждено аварийно прерывать текущий порядок выполнения и немедленно переходить к исполнению ранее сохраненного контекста. В последующих разделах, где пойдет речь о


1.2.2 Характеристики графического контекста

Из книги Язык программирования С# 2005 и платформа .NET 2.0. [3-е издание] автора Троелсен Эндрю

1.2.2 Характеристики графического контекста В предыдущем разделе мы говорили, что GC имеет ряд атрибутов, воздействующих на вывод изображений. Для текста это цвет и шрифт, для линий - цвет и толщина и т.д. Как уже упоминалось выше, атрибуты контекста задаются в момент его


Конвертеры сигнала

Из книги Применение Windows API автора Легалов А И

Конвертеры сигнала Рассмотрим второй, очень важный компонент системы спутникового телевидения – конвертер сигнала. Конвертер представляет собой небольшой электронный блок, находящийся в фокусе спутниковой антенны. Он собирает сигнал, отраженный от зеркала тарелки,


Границы контекста объекта

Из книги Основы AS/400 автора Солтис Фрэнк

Границы контекста объекта Итак, вы могли убедиться, что домены приложения – это логические разделы в рамках процесса, предназначенные для загрузки компоновочных блоков .NET. Домен приложения, в свою очередь, можно делить дальше на контекстные области со своими границами.


Проверка контекста объекта

Из книги Системное программирование в среде Windows автора Харт Джонсон М

Проверка контекста объекта Из тех приложений, которые вы построите сами, очень немногие могут потребовать программного взаимодействия с контекстом, но вот вам пример для иллюстрации подхода, о котором идет речь. Создайте новое консольное приложение с именем ContextManipulator.


Обертка для контекста устройств

Из книги Разработка приложений в среде Linux. Второе издание автора Джонсон Майкл К.

Обертка для контекста устройств Холст или контекст устройств WindowsПеревод А. И. ЛегаловаАнглоязычный оригинал находится на сервере компании Reliable SoftwareЧтобы раукрашивать, рисовать или печатать в окне, Вам необходим контекст устройств (device context или, кратко, DC). DC — это


Аппаратное переключение контекста

Из книги Операционная система UNIX автора Робачевский Андрей М.

Аппаратное переключение контекста Так как только что описанным процедурам обработки исключений может потребоваться доступ к привилегированным командам PowerPC, механизм прерываний должен иметь возможность переключать состояние процессора при передаче управления


Пример: многопоточный поиск контекста

Из книги Linux и UNIX: программирование в shell. Руководство разработчика. автора Тейнсли Дэвид

Пример: многопоточный поиск контекста В программе 6.1 (grepMP) для выполнения одновременного поиска текстового шаблона в нескольких файлах использовались процессы. Программа 7.1 (grepMT), которая включает исходный код функции поиска текстового шаблона grep, обеспечивает


12.1.1. Жизненный цикл сигнала

Из книги Разработка ядра Linux автора Лав Роберт

12.1.1. Жизненный цикл сигнала Сигналы имеют четко определенный жизненный цикл: они создаются, сохраняются до тех пор, пока ядро не выполнит определенное действие на основе сигнала, а затем вызывают совершение этого действия. Создание сигнала называют по-разному: поднятие


20.2. Выдача звукового сигнала

Из книги автора

20.2. Выдача звукового сигнала Заставить консоль генерировать звуковой сигнал в течение определенного периода времени на указанной частоте совсем не сложное дело. Для этого существуют два способа. Первый состоит во включении или отключении постоянной тональной посылки.


Отправление сигнала

Из книги автора

Отправление сигнала Ядро генерирует и отправляет процессу сигнал в ответ на ряд событий, которые могут быть вызваны самим процессом, другим процессом, прерыванием или какими-либо внешними событиями. Можно выделить основные причины отправки сигнала: Особые


26.2.2. Обнаружение сигнала

Из книги автора

26.2.2. Обнаружение сигнала Некоторые сигналы можно захватить и выполнить соответствующие действия. Другие сигналы нельзя уловить. Например, если команда получает сигнал 9, пользователю не нужно предпринимать какие?либо действия.Если ограничиться написанием сценариев,


Вытеснение и переключение контекста

Из книги автора

Вытеснение и переключение контекста Переключение контекста — это переключение от одной, готовой к выполнению задачи к другой. Это переключение производится с помощью функции context_switch(), определенной в файле kernel/sched.c. Данная функция вызывается функцией schedule(), когда новый