Сигналы реального времени всегда ставятся в очередь; каждый такой сигнал, посланный приложению, доставляется ему (если только приложение не прервано перед тем, как такой сигнал будет доставлен). Упорядочение сигналов реального времени также хорошо определено. Сигналы с меньшими номерами всегда доставляются перед сигналами с большими номерами, и когда множество сигналов с одинаковым номером поставлены в очередь, то они доставляются в порядке постановки. Порядок доставки сигналов, не относящихся к расширению реального времени, не определен, как и порядок доставки смеси сигналов реального времени и не относящихся к ним.
Ниже показан пример кода, иллюстрирующий постановку сигналов в очередь и их упорядочивание.
1: /* queued.с */
2:
3: /* получить определение strsignal() из string.h */
4: #define _GNU_SOURCE1
5:
6: #include
7: #include
8: #include
9: #include
10: #include
11:
12: /* Глобальные переменные для построения списка сигналов */
13: int nextSig = 0;
14: int sigOrder[10];
15:
16: /* Перехватить сигнал и записать, что он был обработан */
17: void handler(int signo) {
18: sigOrder[nextSig++] = signo;
19: }
20:
21: int main() {
22: sigset_t mask;
23: sigset_t oldMask;
24: struct sigaction act;
25: int i;
26:
27: /* Обрабатываемые в программе сигналы */
28: sigemptyset(&mask);
29: sigaddset(&mask, SIGRTMIN);
30: sigaddset(&mask, SIGRTMIN+1);
31: sigaddset(&mask, SIGUSR1);
32:
33: /* Отправить сигнал handler() и сохранять их блокированными,
34: чтобы handler() был сконфигурирован во избежание
35: состязаний при манипулировании глобальными переменными */
36: act.sa_handler = handler;
37: act.sa_mask = mask;
38: act.sa_flags = 0;
39:
40: sigaction(SIGRTMIN, &act, NULL);
41: sigaction(SIGRTMIN+1, &act, NULL);
42: sigaction(SIGUSR1, &act, NULL);
43:
44: /* Блокировать сигналы, с которыми мы работаем, чтобы
45: была видна очередность и порядок */
46: sigprocmask(SIG_BLOCK, &mask, &oldMask);
47:
48: /* Генерировать сигналы */
49: raise(SIGRTMIN+1);
50: raise(SIGRTMIN);
51: raise(SIGRTMIN);
52: raise(SIGRTMIN+1);
53: raise(SIGRTMIN);
54: raise(SIGUSR1);
55: raise(SIGUSR1);
56:
57: /* Разрешить доставку этих сигналов. Все они будут доставлены
58: прямо перед возвратом этого вызова (для Linux; это
59: НЕПЕРЕНОСИМОЕ поведение). */
60: sigprocmask(SIG_SETMASK, &oldMask, NULL);
61:
62: /* Отобразить упорядоченный список перехваченных сигналов */
63: printf("Принятые сигналы:\n");
64: for (i = 0; i < nextSig; i++)
65: if (sigOrder[i] < SIGRTMIN)
66: printf("\t%s\n", strsignal(sigOrder[i]));
67: else
68: printf("\tSIGRTMIN + %d\n", sigOrder[i] - SIGRTMIN);
69:
70: return 0;
71: }
Эта программа посылает себе некоторое количество сигналов и выводит на дисплей порядок их получения. Когда сигналы отправляются, она блокирует их, чтобы предотвратить немедленную доставку. Также она блокирует сигналы всякий раз, когда вызывается обработчик, устанавливая значение члена 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] До появления стандарта POSIX приложение могло обращаться к struct sigcontext за информацией того же рода, что теперь представляет siginfo_t , и термин "контекст" остался от этой старой реализации.
сигнала. Приложения, которые должны видеть этот контекст, используют обработчики сигналов, отличающиеся от нормальных. Они включают два дополнительных параметра — указатель на siginfo_t
, предоставляющий контекст сигнала, и указатель на void*
, который может быть использован некоторыми низкоуровневыми системными библиотеками [69] Этот третий параметр на самом деле указывает на структуру struct ucontext , которая позволяет процессам выполнять полное переключение контекстов в пользовательском пространстве. Данные вопросы выходят за пределы тем, рассматриваемых в настоящей книге, но это хорошо документировано в Single Unix Specification.
. Вот как выглядит полный прототип такого обработчика.
Читать дальше