У системного вызова nanosleep() есть аналог, clock_nanosleep(), который приостанавливает вызывающий процесс на определенный отрезок времени (либо пока он не будет прерван сигналом). В этом разделе мы перечислим особенности, которые отличают его от nanosleep().

#define _XOPEN_SOURCE 600

#include

int clock_nanosleep(clockid_t clockid, int flags,

const struct timespec *request, struct timespec *remain);

Возвращает 0 в случае успешного завершения или положительный код, если вызов завершился ошибкой или был прерван сигналом

Аргументы request и remain имеют то же назначение, что и в вызове nanosleep().

Продолжительность сна, заданная в аргументе request, по умолчанию (то есть когда аргумент flags равен 0) является относительной (как и в случае с nanosleep()). Но если среди флагов flags указать константу TIMER_ABSTIME (см. пример в листинге 23.4), значение request станет абсолютным и будет соответствовать времени, измеренному часами clockid. Эта возможность востребована приложениями, которым нужно приостанавливать работу на заданное время с высокой точностью. Если вместо этого получить текущее время, вычислив, сколько осталось до заданного момента, и затем выполнить «относительный переход в режим сна», между любым из этих шагов может возникнуть задержка, которая сделает сон процесса дольше, чем мы того хотели.

Как уже было сказано в подразделе 23.4.2, проблема «затянувшегося сна» особенно остро стоит для процессов, применяющих цикл для возобновления перехода в режим сна, который был прерван обработчиком сигнала. Если сигналы поступают с высокой частотой, относительные значения (которые используются в вызове nanosleep()) могут вызывать существенные отклонения во времени, которое уходит на простаивание процесса. Этой проблемы можно избежать, если в самом начале сделать вызов clock_gettime(), чтобы получить текущее время и добавить к нему необходимое значение, и затем задействовать вызов clock_nanosleep() с флагом TIMER_ABSTIME (перезапуская системный вызов, если он был прерван обработчиком сигнала).

Если установить флаг TIMER_ABSTIME, аргумент remain будет игнорироваться (за ненадобностью). Если вызов clock_nanosleep() прерывается обработчиком сигнала, его можно повторить, используя тот же аргумент request.

Еще одна отличительная черта вызова clock_nanosleep() по сравнению с nanosleep() состоит в том, что мы можем выбрать часы, которые применяются для измерения периода сна. Для этого аргументу clockid можно передать одно из следующих значений: CLOCK_REALTIME, CLOCK_MONOTONIC или CLOCK_PROCESS_CPUTIME_ID. Описание этих часов приводится в табл. 23.1.

В листинге 23.4 показан пример использования вызова clock_nanosleep() для перехода в режим сна на 20 секунд с применением часов CLOCK_REALTIME и абсолютного времени.

Листинг 23.4. Использование вызова clock_nanosleep()

struct timespec request;

/* Получаем текущее значение часов CLOCK_REALTIME */

if (clock_gettime(CLOCK_REALTIME, &request) == –1)

errExit("clock_gettime");

request.tv_sec += 20; /* "Засыпаем" на 20 секунд, начиная с этого момента */

s = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &request, NULL);

if (s!= 0) {

if (s == EINTR)

printf("Interrupted by signal handler\n");

else

errExitEN(s, "clock_nanosleep");

}

23.6. Интервальные таймеры POSIX

Классические интервальные таймеры в UNIX, которые устанавливаются с помощью вызова setitimer(), имеют целый ряд ограничений.

• Мы можем устанавливать лишь по одному таймеру каждого из трех типов: ITIMER_REAL, ITIMER_VIRTUAL и ITIMER_PROF.

• Единственный способ уведомления о срабатывании таймера заключается в передаче сигнала. Кроме того, мы не можем выбрать, какой именно сигнал должен приходить.

• Если во время блокирования соответствующего сигнала интервальный таймер успеет сработать несколько раз, обработчик будет вызван всего лишь один раз. Иными словами, мы не можем узнать, был ли таймер просрочен.

• Таймеры отсчитывают время в микросекундах. Однако некоторые системы поддерживают аппаратные часы с более высокой точностью; в таких случаях желательно иметь программный доступ к точным значениям.

В стандарте POSIX.1b описан программный интерфейс для обхода этих ограничений. В Linux он доступен с версии 2.6.

Жизненный цикл программного интерфейса POSIX-таймеров состоит из следующих этапов.

1. Системный вызов timer_create() создает новый таймер и определяет способ, с помощью которого процесс будет оповещен о его срабатывании.

2. Системный вызов timer_settime() запускает или останавливает таймер.

3. Системный вызов timer_delete() удаляет таймер, который больше не нужен.

POSIX-таймеры не наследуются потомками, которые создаются с помощью вызова fork(). Во время выполнения exec() или при завершении процесса они останавливаются и удаляются.

В Linux программы, которые работают с программным интерфейсом POSIX-таймеров, должны быть скомпилированы с параметром — lrt, иначе их нельзя будет скомпоновать с библиотекой реального времени librt.

Перейти на страницу:

Похожие книги