Опубликован: 06.12.2004 | Доступ: свободный | Студентов: 1179 / 142 | Оценка: 4.76 / 4.29 | Длительность: 20:58:00
ISBN: 978-5-9556-0021-5
Лекция 3:

Мобильное программирование приложений реального времени

Сигналы реального времени

Приложениям реального времени требуются средства надежного, детерминированного, асинхронного извещения (уведомления) о событиях. Теоретически для этого можно было бы разработать совершенно новый механизм, допускающий исключительно эффективную реализацию, однако авторы стандарта 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 ), они и будут использоваться для контроля времени ожидания.

Если несколько потоков управления ждут один сигнал, он достанется кому-то одному из них.