Программирование в стандарте POSIX

       

Работа с интервальными таймерами


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

Описываемые далее средства для работы с интервальными таймерами входят в необязательную часть стандарта POSIX-2001, именуемую "X/Open-расширение системного интерфейса" (XSI).

Реализация должна предоставлять каждому процессу по крайней мере три интервальных таймера, обозначаемых следующими идентификаторами.

ITIMER_REAL

Таймер реального времени. Он ассоциирован с часами CLOCK_REALTIME и, следовательно, его показания уменьшаются в реальном масштабе времени. Когда он срабатывает, процессу доставляется сигнал SIGALRM.

ITIMER_VIRTUAL

Таймер виртуального времени процесса. Его показания уменьшаются в соответствии с течением виртуального времени процесса, т. е. только тогда, когда процесс выполняется. При срабатывании таймера процессу доставляется сигнал SIGVTALRM.

ITIMER_PROF

Таймер профилирования. Показания этого таймера уменьшаются с течением виртуального времени процесса, а также тогда, когда работают запрошенные им системные сервисы. Он предназначен для построения статистическими методами профилей расходования процессом процессорного времени. При его срабатывании процессу доставляется сигнал SIGPROF.

Согласно стандарту POSIX-2001, интервальные таймеры обслуживаются функциями getitimer() и setitimer() (см. листинг 12.32).

#include <sys/time.h> int getitimer (int timer_id, struct itimerval *cvalue); int setitimer (int timer_id, const struct itimerval *restrict nvalue, struct itimerval *restrict ovalue);

Листинг 12.32. Описание функций getitimer() и setitimer(). (html, txt)

Поддерживаемые стандартом значения аргумента timer_id перечислены выше. Другие характеристики интервальных таймеров задаются в структуре типа itimerval, которая должна содержать по крайней мере следующие поля:

struct timeval it_interval; /* Интервал таймера */

struct timeval it_value; /* Текущие показания таймера */ /* (ведется обратный отсчет) */


(Напомним, что структура типа timeval была описана выше. Она содержит по крайней мере два поля, задающие время, соответственно, в секундах и микросекундах.)

Значение поля it_value (если оно отлично от нуля) показывает время, оставшееся до срабатывания таймера. После срабатывания таймер запускается вновь с начальным значением поля it_value, равным it_interval (если последнее отлично от нуля).

Установка нулевого значения в поле it_value, независимо от величины it_interval, снимает таймер со взвода. Если сделать нулевым значение it_interval, таймер будет разряжен после очередного срабатывания. Таким способом можно реализовать таймер с ограниченной периодичностью (в частности, одноразовый).

Функция getitimer() запоминает текущие характеристики таймера с заданным идентификатором в структуре, на которую указывает аргумент cvalue. Функция setitimer() - ее можно назвать многоцелевой - взводит или снимает таймер со взвода, устанавливает новые характеристики, пользуясь значением аргумента nvalue, и сохраняет старые по указателю ovalue (если он отличен от NULL).

Признаком успешного завершения обеих функций является нулевой результат.

Здесь уместно акцентировать внимание на одной тонкости, которую до сих пор мы старательно замалчивали. Поскольку разрешающая способность любых часов конечна, для показаний часов и таймеров можно установить только те значения, которые кратны этой разрешающей способности. Если соответствующие аргументы функций clock_settime(), nanosleep() или setitimer() не удовлетворяют данному условию, производится округление. Для часов используется наилучшее приближение снизу, для длительности приостановки выполнения и таймеров - наилучшее приближение сверху (стандарт POSIX-2001 требует, чтобы длительности приостановки выполнения и промежутка времени до срабатывания таймера были не меньше заказанных).

Последнее обстоятельство позволяет выяснить размер такта часов реального времени довольно неожиданным образом (впрочем, вполне стандартным и мобильным с точки зрения спецификаций POSIX-2001) (см.


листинг 12.33).

Листинг 12.33. Пример программы, использующей функции getitimer() и setitimer(). (html, txt)

Здесь интервальный таймер не взводится (поскольку значение it_value - нулевое) и, соответственно, не срабатывает. Он нужен лишь для того, чтобы операционная система подкорректировала должным образом (с учетом разрешающей способности часов реального времени) значение поля it_interval.tv_usec . В результате корректировки первоначально присвоенная этому полю единица может превратиться, например, в 10000 (микросекунд), что дает тактовую частоту часов реального времени 100 Гц.

Следующая программа (см. листинг 12.34) сравнивает ход реального и виртуального времени процесса, определяя, сколько реального времени прошло за один квант виртуального (в данном случае квант - это 100000 мксек, т. е. 0.1 сек). Колебания показаний, естественно, вызываются нагрузкой на процессор, индуцируемой другими, параллельно работающими процессами. В принципе, целенаправленная организация подобных колебаний и их анализ могут быть использованы для создания так называемых скрытых каналов по времени (см. [7]).

Листинг 12.34. Пример программы, использующей интервальные таймеры реального и виртуального времени. (html, txt)

Результаты работы программы могут выглядеть так, как показано в листинге 12.35. Можно видеть, что, во-первых, доля процессорного времени, которая достается процессу, может существенно колебаться (см., например, отсчеты 4, 6 и 8) и, во-вторых, пока выполнение процесса приостановлено, его виртуальное время также стоит (см. инструкцию sleep (5) в листинге 12.34, отсчет 1 в результатах).

Прошедшее реальное время за один квант виртуального 1 5255378 мксек 2 249993 мксек 3 250003 мксек 4 100000 мксек 5 250177 мксек 6 859831 мксек 7 100054 мксек 8 729943 мксек 9 580003 мксек 10 99989 мксек 11 580129 мксек 12 429881 мксек 13 99990 мксек

Листинг 12.35. Возможные результаты работы программы, использующей интервальные таймеры реального и виртуального времени. (html, txt)


Содержание раздела