7.3.6 Сигналы реального времени
Сигналы расширения POSIX 1003.1b играют очень важную роль в приложениях реального времени. Они используются для уведомления процессов о возникновении асинхронных событий, таких как завершение работы таймера высокого разрешения, завершение асинхронного ввода/вывода, приём сообщения в пустой очереди сообщений POSIX, и так далее. Некоторые преимущества, которыми обладают сигналы реального времени по отношению к нативным сигналам, перечислены в Таблице 7.7. Эти преимущества делают их пригодными для приложений реального времени. Сигнальные интерфейсы реального времени POSIX.1b перечислены в Таблице 7.8.
Таблица 7.7 Сравнение сигналов реального времени и нативных сигналов
Сигналы реального времени
Диапазон предназначенных для приложения сигналов от SIGRTMIN до SIGRTMAX. Все сигналы реального времени определяются в этом диапазоне, например, SIGRTMIN + 1, SIGRTMIN + 2, SIGRTMAX – 2, и так далее.
Только два предназначенных для приложения сигнала, SIGUSR1 и SIGUSR2 .
Доставка сигналов может иметь приоритет. Чем меньше номер сигнала, тем выше приоритет. Например, если ждут обработки сигналы SIGRTMIN и SIGRTMIN + 1, то первым будет доставлен SIGRTMIN.
Нет приоритета для доставки сигнала.
Отправитель может передать принимающему процессу вместе с сигналом реального времени дополнительную информацию.
Вместе с сигналом нельзя послать дополнительную информацию.
Сигналы поступают в очередь (то есть, если сигнал доставлен процессу несколько раз, получатель будет обрабатывать все экземпляры сигнала). Сигналы реального времени не теряются.
Сигналы могут потеряться. Если сигнал доставлен процессу несколько раз, получатель обработает только один экземпляр.
Таблица 7.8 Функции сигналов реального времени POSIX.1b
Регистрация дескриптора сигнала и механизма уведомления.
Отправка процессу сигнала и дополнительной информации.
Ожидание доставки сигнала.
Ожидание доставки сигнала и завершение по истечении времени ожидания, если сигнал не прибыл.
Приостановка процесса, пока не получен сигнал.
Изменение текущей маски блокировки процесса.
Добавление сигнала к набору сигналов.
Удаление сигнала из набора сигналов.
Очистка набора сигналов от всех сигналов.
Установка всех сигналов в наборе сигналов.
Проверка, является ли сигнал членом набора сигналов.
Объясним вышеописанные интерфейсы на примере. В этом примере родительский процесс посылает сигналы реального времени дочернему процессу, а затем они обрабатываются. Пример разделён на две части, как показано на Рисунке 7.4.
Рисунок 7.4 Сигналы реального времени: пример приложения.
void rt_handler(int signum, siginfo_t *siginfo,
Родительский процесс использует для отправки дочернему процессу SIGRTMIN и SIGRTMIN + 1 функцию sigqueue . Последний аргумент функции используется, чтобы передавать вместе с сигналом дополнительную информацию.
/* Спим, пока запускается дочерний процесс */
/* Дополнительная информация для SIGRTMIN */
/* Отправка дочернему процессу SIGRTMIN */
sigqueue(cpid, SIGRTMIN, value);
/* Отправка дочернему процессу SIGRTMIN+1 */
sigqueue(cpid, SIGRTMIN+1, value);
/* Наконец, посылаем SIGRTMIN ещё раз */
sigqueue(cpid, SIGRTMIN, value);
Для хранения сигналов, которые необходимо блокировать, мы определяем маску типа sigset_t . Прежде чем продолжить, очищаем маску, вызывая sigemptyset . Эта функция инициализирует маску, чтобы исключить все сигналы. Затем вызывается sigaddset , чтобы добавить к заблокированному набору сигналы SIGRTMIN и SIGRTMIN + 1 . Наконец, для блокировки доставки сигнала вызывается sigprocmask . (Мы блокируем эти сигналы в процессе их доставки с помощью функции ожидания сигнала, вместо того, чтобы использовать обработчик сигнала.)
/* Добавляем к маске SIGRTMIN */
/* Добавляем к маске SIGRTMIN+1 */
* Блокируем доставку сигналов SIGRTMIN, SIGRTMIN+1.
* После возвращения предыдущее значение
* заблокированной сигнальной маски хранится в oldmask
Теперь дочерний процесс ждёт доставки SIGRTMIN и SIGRTMIN + 1 . Для ожидания блокированных сигналов используются функция sigwaitinfo или sigtimedwait . Набором сигналов для ожидания является первый аргумент. Второй аргумент заполняется любой дополнительной информацией, полученной вместе с сигналом (подробнее об siginfo_t позже). Последним аргументом для sigtimedwait является время ожидания.
/* Задаём время ожидания 1 с */
* Ждём доставки сигнала. Мы ожидаем 2 сигнала,
* SIGRTMIN и SIGRTMIN+1. Цикл завершится, когда
* будут приняты оба сигнала
if ((recv_sig = sigtimedwait(&mask, &siginfo, &tv)) == -1)
printf(«signal %d received\n», recv_sig);
Другим методом для обработки сигналов является регистрация обработчика сигналов. В этом случае процесс не должен блокировать сигнал. Зарегистрированный обработчик сигнала вызывается, когда сигнал доставляется процессу. Обработчик сигнала регистрирует функция sigaction . Это второй аргумент структуры sigaction , определённой как
void (*sa_sigaction)(int, siginfo_t *, void *);
Полями этой структуры являются:
▪ sa_handler : функция регистрации обработчика сигналов для сигналов не реального времени.
▪ sa_sigaction : функция регистрации обработчика сигналов для сигналов реального времени.
▪ sa_mask : маска сигналов, которые должны быть заблокированы, когда выполняется обработчик сигналов.
▪ sa_flags : для сигналов реального времени используется SA_SIGINFO . При получении сигнала, который имеет установленный SA_SIGINFO , вместо sa_handler вызывается функция sa_sigaction .
Теперь для обработки сигнала реального времени SIGRTMIN дочерний процесс регистрирует обработчик сигналов rt_handler . Эта функция будет вызываться, когда дочернему процессу доставляется SIGRTMIN .
* Регистрируем обработчик сигнала для SIGRTMIN.
* Обратите внимание, что для регистрации
* обработчика мы используем интерфейс
if (sigaction(SIGRTMIN, &action, NULL) == -1)
Затем дочерний процесс ждёт доставки сигнала. sigsuspend временно заменяет текущую маску сигнала маской, указанной в её аргументе. Затем она ожидает доставки разблокированного сигнала. После доставки сигнала sigsuspend восстанавливает исходную маску и возвращается после выполнения обработчика сигнала. Если обработчик сигнала вызывает прекращение процесса, sigsuspend не возвращается.
Таким образом, дочерний процесс успешно обработал сигналы, используя и функции ожидания сигнала, и обработчик сигналов.
Наконец, обработчик сигнала:
/* Обработчик сигнала для SIGRTMIN */
void rt_handler(int signum, siginfo_t *siginfo,
printf(«signal %d received. code = %d, value = %d\n»,
Второй аргумент обработчика сигнала имеет тип siginfo_t и содержит всю информацию о полученном сигнале. Эта структура определяется так:
int si_signo; // Номер сигнала
int si_errno; // Ошибка сигнала
Чтобы узнать о всех полях этой структуры, обратитесь, пожалуйста, к /usr/include/bits/siginfo.h . Кроме si_signo , si_errno и si_code все остальные поля входят в объединение. Так что следует читать только те поля, которые имеют смысл в данном контексте. Например, поля, приведённые выше, действительны только для сигналов POSIX.1b.
▪ si_signo является номером сигнала принятого сигнала. Это то же самое, что и первый аргумент обработчика сигналов.
▪ si_code передаёт источник сигнала. Важные значения si_code для сигналов реального времени перечислены в Таблице 7.9.
▪ si_value это дополнительная информация, передаваемая отправителем.
▪ pid и uid являются идентификатором процесса и идентификатором пользователя отправителя, соответственно.