Мобильное программирование приложений реального времени
Сигналы реального времени
Приложениям реального времени требуются средства надежного, детерминированного, асинхронного извещения (уведомления) о событиях. Теоретически для этого можно было бы разработать совершенно новый механизм, допускающий исключительно эффективную реализацию, однако авторы стандарта POSIX-2001 не пошли по такому пути по той простой причине, что уже имеется механизм сигналов, который обладает почти всеми необходимыми свойствами. "Почти", потому что он не решает следующих проблем.
- Не обеспечивается надежная доставка уведомлений о событиях. Если имеется ждущий сигнал, то при последующем появлении таких же сигналов их доставка не гарантируется.
- Не поддерживается ранжированная по приоритетам доставка уведомлений о событиях. Если имеется несколько ждущих неблокированных сигналов, порядок их доставки не определен.
- Не поддерживается дифференциация между сигналами одного типа.
Чтобы устранить перечисленные недостатки, механизм сигналов был расширен, а в стандарт была введена необязательная часть, получившая название " сигналы реального времени " (Realtime Signals Extension, RTS).
Номера сигналов реального времени лежат в диапазоне от SIGRTMIN до SIGRTMAX. Всего таких сигналов должно быть не меньше, чем RTSIG_MAX. Обеспечивается ли поведение в реальном времени для других сигналов, зависит от реализации.
"Жизненный цикл" сигналов реального времени состоит из четырех фаз:
- генерация;
- ожидание;
- доставка;
- обработка.
Сигналы реального времени могут генерироваться при срабатывании высокоточных таймеров, завершении операции асинхронного ввода/вывода, поступлении межпроцессного сообщения, выполнении функции sigqueue() и т.д.
На фазе генерации сигналов центральную роль играет определенная в заголовочном файле <signal.h> структура типа sigevent, которая, согласно стандарту, должна содержать по крайней мере следующие поля.
int sigev_notify; /* Способ уведомления */ int sigev_signo; /* Номер сигнала */ union sigval sigev_value; /* Значение сигнала */ void (*) (union sigval) sigev_notify_function; /* Функция уведомления */ (pthread_attr_t *)sigev_notify_attributes; /* Атрибуты уведомления */
Значение поля sigev_notify определяет способ уведомления об асинхронном событии.
Константа SIGEV_NONE означает отсутствие уведомления ( событие проходит незамеченным).
Константа SIGEV_SIGNAL предписывает сгенерировать сигнал с номером sigev_signo. Если для этого сигнала установлен флаг SA_SIGINFO, он ( сигнал ) ставится в очередь к процессу. Тем самым обеспечивается надежная доставка уведомлений о событиях.
Константа SIGEV_THREAD задает в качестве механизма уведомления вызов функции.
Чтобы обеспечить дифференциацию между сигналами одного типа, с ними ассоциируется значение, которое задается при генерации сигнала либо в явном виде как отдельный аргумент соответствующей функции, либо как значение поля sigev_value структуры-аргумента типа sigevent.
Значение сигнала реального времени может быть целым числом или указателем, поскольку объединение типа sigval должно определяться со следующими полями:
int sigval_int; /* Значение сигнала – целое число */ void *sigval_ptr; /* Значение сигнала – указатель */
Если для многопотоковой программы в качестве способа уведомления о наступлении события избран вызов функции (константа SIGEV_THREAD в поле sigev_notify ), то указатель на эту функцию извлекается из поля sigev_notify_function, а ее аргументом служит значение сигнала. Эта функция выполняется как стартовая для вновь созданного обособленного потока управления с атрибутным объектом *sigev_notify_attributes.
При подобном способе уведомления сигналы как таковые не генерируются, а уведомляющую функцию не следует путать с функцией обработки сигнала, которая вызывается в ином контексте и с другими аргументами.
После того, как сигнал сгенерирован функцией sigqueue() или иным способом, позволяющим задать определяемое приложением значение, наступает фаза ожидания. Сигналы одного типа ставятся в очередь к процессу в порядке генерации.
Сигналы, сгенерированные с помощью вызова функции kill() или в результате наступления такого события, как аппаратное прерывание, срабатывание будильника или ввод управляющего символа с терминала, для которых реализация не обеспечивает постановку в очередь, никак на эту очередь не влияют.
При наличии нескольких неблокированных ждущих сигналов реального времени, их доставка процессу производится в порядке возрастания номеров. Тем самым поддерживается ранжированная по приоритетам доставка уведомлений, а роль приоритета играет номер сигнала.
На фазе обработки уведомления об асинхронном событии используется структура типа siginfo_t. Для сигналов реального времени она включает, помимо описанных в курсе [1], дополнительный элемент
union sigval si_value; /* Значение сигнала */
в который переносится значение из поля sigev_value структуры типа sigevent.
Подразумеваемые действия при обработке сигнала реального времени ( SIG_DFL ) состоят в аварийном завершении процесса.
Простейший способ сгенерировать сигнал реального времени – обратиться к функции sigqueue() (см. листинг 3.12).
#include <signal.h> int sigqueue (pid_t pid, int signo, const union sigval value);Листинг 3.12. Описание функции sigqueue().
Функция sigqueue() посылает сигнал с номером signo и значением value процессу, идентификатор которого задан аргументом pid. Права на посылку сигнала определяются так же, как и для функции kill() ; аналогично kill(), при нулевом значении signo сигнал не посылается, а проверяется существование процесса с идентификатором pid.
Функция sigqueue() завершается немедленно, без какого-либо ожидания. Если для сигнала signo установлен флаг SA_SIGINFO и в наличии достаточно ресурсов, сигнал ставится в очередь к процессу pid. Если флаг SA_SIGINFO не установлен, сигнал посылается процессу-получателю по крайней мере один раз, но, возможно, без ассоциированного с ним значения.
Если процесс посылает сигнал сам себе, он ( сигнал ) будет доставлен вызывающему потоку управления до выхода из функции sigqueue().
Дождаться доставки сигнала реального времени можно с помощью функций sigwaitinfo() и sigtimedwait() (см. листинг 3.13), являющихся аналогами рассмотренной в курсе [1] функции sigwait().
#include <signal.h> int sigwaitinfo ( const sigset_t *restrict set, siginfo_t *restrict info); int sigtimedwait ( const sigset_t *restrict set, siginfo_t *restrict info, const struct timespec *restrict timeout);Листинг 3.13. Описание функций sigwaitinfo() и sigtimedwait().
Данные функции возвращают в качестве нормального результата номер полученного сигнала, входящего в набор set. Кроме того, если значение аргумента info отлично от NULL, заполняются поля si_signo (номер сигнала ), si_code (источник сигнала ) и, возможно, si_value (значение, если оно ассоциировано с сигналом ) указуемого объекта структурного типа siginfo_t. Разумеется, полученный сигнал изымается из очереди ждущих, а соответствующие ресурсы освобождаются.
Функция sigtimedwait() отличается тем, что ограничивает время ожидания сигнала заданным интервалом. Если аргумент timeout является пустым указателем, поведение функции не специфицировано. Если реализация поддерживает монотонные часы ( CLOCK_MONOTONIC ), они и будут использоваться для контроля времени ожидания.
Если несколько потоков управления ждут один сигнал, он достанется кому-то одному из них.