У системного вызова nanosleep() есть аналог, clock_nanosleep(), который приостанавливает вызывающий процесс на определенный отрезок времени (либо пока он не будет прерван сигналом). В этом разделе мы перечислим особенности, которые отличают его от nanosleep().
#define _XOPEN_SOURCE 600
#include
int clock_nanosleep(clockid_t
const struct timespec *
Возвращает 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");
}
Классические интервальные таймеры в 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.