timerfd_create, timerfd_settime, timerfd_gettime -
таймеры,
уведомляющие
через
файловые
дескрипторы
Standard C library (
libc,
-lc)
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *_Nullable old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);
Эти
системные
вызовы
создают и
работают с
таймером,
доставляя
уведомления
о
срабатывании
через
файловый
дескриптор.
Они
предоставляют
альтернативу
setitimer(2) или
timer_create(2);
их
преимущество
в том, что за
файловым
дескриптором
можно
следить с
помощью
select(2),
poll(2) и
epoll(7).
Использование
данных
трёх
системных
вызовов
аналогично
timer_create(2),
timer_settime(2) и
timer_gettime(2)
(аналога
timer_getoverrun(2) нет, так
как его
возможности
предоставляет
read(2) как
описано
ниже).
Вызов
timerfd_create()
создаёт
новый
объект
таймера и
возвращает
файловый
дескриптор,
который
ссылается
на этот
таймер. В
аргументе
clockid
задаются
часы,
которые
используются
для хода
таймера;
значением
должно
быть одно
из
следующих:
- CLOCK_REALTIME
- Настраиваемые
системные
часы
реального
времени.
- CLOCK_MONOTONIC
- Ненастраиваемые,
постоянно
идущие
вперёд
часы,
отсчитывающие
время с
некоторой
неопределённой
точки в
прошлом,
которая не
изменяется
с момент
запуска
системы.
-
CLOCK_BOOTTIME
(начиная с Linux
3.15)
- Подобно
CLOCK_MONOTONIC,
представляет
монотонно
растущие
часы.
Однако,
если часы
CLOCK_MONOTONIC не
отсчитывают
время
когда
система
находится
в
состоянии
ожидания
(suspended), а часы
CLOCK_BOOTTIME
учитывают
время в
таком
состоянии
системы.
Это
полезно
приложениям,
которым
необходимо
учитывать
состояние
ожидания.
CLOCK_REALTIME не
подходят
для таких
приложений,
так как эти
часы
подвержены
скачкообразным
изменениям
системных
часов.
-
CLOCK_REALTIME_ALARM
(начиная с Linux
3.11)
- Эти часы
подобны
CLOCK_REALTIME, но
разбудят
систему,
если она
находится
с
состоянии
ожидания.
Для
установки
таймера по
этим часам
вызывающий
должен
иметь
мандат
CAP_WAKE_ALARM.
-
CLOCK_BOOTTIME_ALARM
(начиная с Linux
3.11)
- Эти часы
подобны
CLOCK_BOOTTIME, но
разбудят
систему,
если она
находится
с
состоянии
ожидания.
Для
установки
таймера по
этим часам
вызывающий
должен
иметь
мандат
CAP_WAKE_ALARM.
See
clock_getres(2) for some further details on the above clocks.
Текущее
значение
каждого из
этих часов
можно
получить с
помощью
clock_gettime(2).
Начиная с Linux 2.6.27,
для
изменения
поведения
timerfd_create() можно
использовать
следующие
значения
flags
(через OR):
- TFD_NONBLOCK
- Устанавливает
флаг
состояния
файла O_NONBLOCK
для нового
открытого
файлового
описания
(смотрите
open(2)), на
которое
ссылается
новый
файловый
дескриптор.
Использование
данного
флага
делает
ненужными
дополнительные
вызовы fcntl(2)
для
достижения
того же
результата.
- TFD_CLOEXEC
- Устанавливает
флаг close-on-exec ( FD_CLOEXEC)
для нового
открытого
файлового
дескриптора.
Смотрите
описание
флага O_CLOEXEC в
open(2) для того,
чтобы
узнать как
это может
пригодиться.
До Linux 2.6.26
включительно
аргумент
flags
должен
быть равен
нулю.
Вызов
timerfd_settime()
запускает
или
останавливает
таймер, на
который
ссылается
файловый
дескриптор
fd.
The
new_value argument specifies the initial expiration and interval for
the timer. The
itimerspec structure used for this argument is described
in
itimerspec(3type).
В
new_value.it_value
задаётся
первое
срабатывание
таймера, в
секундах и
наносекундах.
Установка
любого из
полей
new_value.it_value в
ненулевое
значение
включает
таймер.
Установка
обоих
полей
new_value.it_value в
ноль
выключает
таймер.
Установка
одного или
обоих
полей
new_value.it_interval в
ненулевое
значение
задаёт
период, в
секундах и
наносекундах,
периодического
срабатывания
таймера
после
первого
срабатывания.
Если оба
поля
new_value.it_interval
равны нулю,
то таймер
срабатывает
только
один раз, во
время,
указанное
в
new_value.it_value.
По
умолчанию,
начальное
время
срабатывания,
задаваемое
в
new_value,
считается
относительно
текущего
времени
часов
таймера на
момент
вызова (т. е.,
в
new_value.it_value
задаётся
время
относительно
текущего
значения
часов,
заданных в
clockid).
Использование
абсолютной
задержки
можно
включить
через
аргумент
flags.
Аргумент
flags
является
битовой
маской,
которая
может
включать
следующие
значения:
- TFD_TIMER_ABSTIME
- Считать
new_value.it_value
абсолютным
значением
часов
таймера.
Таймер
сработает,
когда
значение
часов
таймера
достигнет
значения,
указанного
в new_value.it_value.
- TFD_TIMER_CANCEL_ON_SET
- Если
этот флаг
указан
вместе с
TFD_TIMER_ABSTIME и
часами
этого
таймера
являются
CLOCK_REALTIME или
CLOCK_REALTIME_ALARM, то
помечать
этот
таймер как
отменяемый,
если часы
реального
времени
подвергнуться
скачкообразному
изменению (
settimeofday(2), clock_settime(2) или
подобными).
Когда
такие
изменения
происходят,
текущий
или
будущий
вызова read(2)
для
файлового
дескриптора
завершается
с ошибкой
ECANCELED.
Если
old_value не
равно NULL, то
это
указатель
на
структуру
itimerspec, и он
будет
использоваться
для
возврата
текущих на
момент
вызова
настроек
таймера;
смотрите
описание
timerfd_gettime() далее.
Вызов
timerfd_gettime()
возвращает
в
curr_value,
которое
указывает
на
структуру
itimerspec, текущие
настройки
таймера, на
который
ссылается
файловый
дескриптор
fd.
В поле
it_value
возвращается
время до
следующего
срабатывания
таймера.
Если оба
поля этой
структуры
равны нулю,
то таймер в
данный
момент не
запущен.
Это поле
всегда
содержит
относительное
значение,
независимо
от того, был
ли указан
флаг
TFD_TIMER_ABSTIME
при
настройке
таймера.
В поле
it_interval
возвращается
интервал
таймера.
Если оба
поля этой
структуры
равны нулю,
то таймер
настроен
на
однократное
срабатывание,
на время,
заданное в
curr_value.it_value.
The file descriptor returned by
timerfd_create() supports the following
additional operations:
-
read(2)
- If the timer has already expired one or more times since
its settings were last modified using timerfd_settime(), or since
the last successful read(2), then the buffer given to
read(2) returns an unsigned 8-byte integer ( uint64_t)
containing the number of expirations that have occurred. (The returned
value is in host byte order—that is, the native byte order for
integers on the host machine.)
- Если
таймер ещё
не
срабатывал
до вызова
read(2), то вызов
блокирует
выполнение
до
следующего
срабатывания
таймера,
или
завершается
с ошибкой
EAGAIN, если
файловый
дескриптор
был создан
неблокирующим
(с помощью
вызова fcntl(2) и
операции
F_SETFL с флагом
O_NONBLOCK).
- Вызов read(2)
завершается
ошибкой EINVAL,
если
размер
указанного
буфера
будет
меньше 8
байт.
- Если
используются
часы CLOCK_REALTIME
или CLOCK_REALTIME_ALARM,
таймер
является
абсолютным
( TFD_TIMER_ABSTIME) и при
вызове timerfd_settime()
указан
флаг TFD_TIMER_CANCEL_ON_SET,
то read(2)
завершается
ошибкой ECANCELED,
если часы
реального
времени
подвергнуться
скачкообразному
изменению
(это
позволяет
читающему
приложению
обнаружить
такие
скачкообразные
изменения
часов).
- If the associated clock is either CLOCK_REALTIME or
CLOCK_REALTIME_ALARM, the timer is absolute
(TFD_TIMER_ABSTIME), and the flag TFD_TIMER_CANCEL_ON_SET
was not specified when calling timerfd_settime(), then a
discontinuous negative change to the clock (e.g., clock_settime(2))
may cause read(2) to unblock, but return a value of 0 (i.e., no
bytes read), if the clock change occurs after the time expired, but before
the read(2) on the file descriptor.
-
poll(2), select(2) (и
подобные)
- Файловый
дескриптор
доступен
для чтения
(в select(2)
аргумент
readfds; в poll(2) флаг
POLLIN), если
произошло
одно или
более
срабатываний
таймера.
- Файловый
дескриптор
также
поддерживает
другие
мультиплексные
вызовы: pselect(2),
ppoll(2) и epoll(7).
-
ioctl(2)
- Поддерживается
следующая
команда,
относящаяся
к timerfd:
-
TFD_IOC_SET_TICKS
(начиная с Linux
3.17)
- Корректирует
количество
истечений
таймера,
которые
произошли.
Аргументом
является
указатель
на
ненулевое
8-байтовое
целое ( uint64_t*),
содержащее
новое
количество
истечений.
После
установки
количества,
все
ожидающие
таймера
пробуждаются.
Единственная
цель
данной
команды —
восстановить
истечений
для
отсечки/восстановления.
Данная
операция
доступна
только,
если ядро
собрано с
параметром
CONFIG_CHECKPOINT_RESTORE.
-
close(2)
- Если
файловый
дескриптор
больше не
требуется,
его нужно
закрыть.
Когда все
файловые
дескрипторы,
связанные
с одним
объектом
таймера,
будут
закрыты,
таймер
выключается
и ядро
освобождает
его
ресурсы.
После
fork(2)
потомки
наследуют
копию
файлового
дескриптора,
созданного
timerfd_create().
Файловый
дескриптор
потомка
ссылается
на тот же
объект
таймера,
что и
файловый
дескриптор
его
родителя, и
операция
read(2) в
потомке
будет
возвращать
информацию
о
срабатываниях
таймера.
Файловый
дескриптор,
созданный
timerfd_create(),
сохраняется
при
execve(2), и
продолжает
генерировать
срабатывания
таймера,
если он
включён.
При
успешном
выполнении
timerfd_create()
возвращает
новый
файловый
дескриптор.
При ошибке
возвращается
-1, и
errno
устанавливается
в
соответствующее
значение.
При
успешном
выполнении
timerfd_settime() и
timerfd_gettime()
возвращают
0; в случае
ошибки
возвращается
-1, а
errno
устанавливается
в
соответствующее
значение
ошибки.
Вызов
timerfd_create()
может
завершиться
со
следующими
ошибками:
- EINVAL
- The clockid is not valid.
- EINVAL
- Неправильное
значение
flags или, для Linux
2.6.26 и старее,
flags не равно
0.
- EMFILE
- Было
достигнуто
ограничение
по
количеству
открытых
файловых
дескрипторов
на
процесс.
- ENFILE
- Достигнуто
максимальное
количество
открытых
файлов в
системе.
- ENODEV
- Не
удалось
смонтировать
(внутреннее)
безымянное
устройство
inode.
- ENOMEM
- Недостаточно
памяти
ядра для
создания
таймера.
- EPERM
-
clockid was CLOCK_REALTIME_ALARM or
CLOCK_BOOTTIME_ALARM but the caller did not have the
CAP_WAKE_ALARM capability.
Вызовы
timerfd_settime()
и
timerfd_gettime() могут
завершаться
со
следующими
ошибками:
- EBADF
- Значение
fd не
является
правильным
файловым
дескриптором.
- EFAULT
- Некорректный
указатель
new_value, old_value или
curr_value.
- EINVAL
- Значение
fd не
является
правильным
файловым
дескриптором
timerfd.
Вызов
timerfd_settime()
также
может
завершиться
со
следующими
ошибками:
- ECANCELED
- Смотрите
ЗАМЕЧАНИЯ.
- EINVAL
- Значение
new_value
некорректно
инициализировано
(одно из tv_nsec
вне
диапазона
от 0 до 999999999).
- EINVAL
- Значение
flags
неверно.
These system calls are available since Linux 2.6.25. Library support is provided
since glibc 2.8.
Данные
системные
вызовы
есть
только в Linux.
Suppose the following scenario for
CLOCK_REALTIME or
CLOCK_REALTIME_ALARM timer that was created with
timerfd_create():
- (1)
- The timer has been started (timerfd_settime()) with
the TFD_TIMER_ABSTIME and TFD_TIMER_CANCEL_ON_SET
flags;
- (2)
- A discontinuous change (e.g., settimeofday(2)) is
subsequently made to the CLOCK_REALTIME clock; and
- (3)
- the caller once more calls timerfd_settime() to
rearm the timer (without first doing a read(2) on the file
descriptor).
In this case the following occurs:
- •
- The timerfd_settime() returns -1 with errno
set to ECANCELED. (This enables the caller to know that the
previous timer was affected by a discontinuous change to the clock.)
- •
- The timer is successfully rearmed with the settings
provided in the second timerfd_settime() call. (This was probably
an implementation accident, but won't be fixed now, in case there are
applications that depend on this behaviour.)
В
настоящее
время
timerfd_create()
поддерживает
только
несколько
типов
идентификаторов
часов,
поддерживаемых
timer_create(2).
Следующая
программа
создаёт
таймер и
затем
следит за
его
работой.
Программа
получает
до трёх
аргументов
из
командной
строки. В
первом
аргументе
задаётся
количество
секунд до
первого
срабатывания
таймера. Во
втором
аргументе
задаётся
интервал
таймера в
секундах. В
третьем
аргументе
задаётся
сколько
программа
должна
позволить
сработать
таймеру до
завершения.
Второй и
третий
аргументы
необязательны.
Следующий
сеанс
работы в
оболочке
показывает
работу с
программой:
$ a.out 3 1 100
0.000: timer started
3.000: read: 1; total=1
4.000: read: 1; total=2
^Z # type control-Z to suspend the program
[1]+ Stopped ./timerfd3_demo 3 1 100
$ fg # Resume execution after a few seconds
a.out 3 1 100
9.660: read: 5; total=7
10.000: read: 1; total=8
11.000: read: 1; total=9
^C # type control-C to suspend the program
#include <err.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
static void
print_elapsed_time(void)
{
int secs, nsecs;
static int first_call = 1;
struct timespec curr;
static struct timespec start;
if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
err(EXIT_FAILURE, "clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
err(EXIT_FAILURE, "clock_gettime");
secs = curr.tv_sec - start.tv_sec;
nsecs = curr.tv_nsec - start.tv_nsec;
if (nsecs < 0) {
secs--;
nsecs += 1000000000;
}
printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
}
int
main(int argc, char *argv[])
{
int fd;
ssize_t s;
uint64_t exp, tot_exp, max_exp;
struct timespec now;
struct itimerspec new_value;
if (argc != 2 && argc != 4) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
err(EXIT_FAILURE, "clock_gettime");
/* Создаём абсолютный таймер CLOCK_REALTIME с начальным
срабатыванием и интервалом, заданными из командной строки. */
new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
new_value.it_value.tv_nsec = now.tv_nsec;
if (argc == 2) {
new_value.it_interval.tv_sec = 0;
max_exp = 1;
} else {
new_value.it_interval.tv_sec = atoi(argv[2]);
max_exp = atoi(argv[3]);
}
new_value.it_interval.tv_nsec = 0;
fd = timerfd_create(CLOCK_REALTIME, 0);
if (fd == -1)
err(EXIT_FAILURE, "timerfd_create");
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
err(EXIT_FAILURE, "timerfd_settime");
print_elapsed_time();
printf("таймер запущен\n");
for (tot_exp = 0; tot_exp < max_exp;) {
s = read(fd, &exp, sizeof(uint64_t));
if (s != sizeof(uint64_t))
err(EXIT_FAILURE, "read");
tot_exp += exp;
print_elapsed_time();
printf("read: %" PRIu64 "; total=%" PRIu64 "\n", exp, tot_exp);
}
exit(EXIT_SUCCESS);
}
eventfd(2),
poll(2),
read(2),
select(2),
setitimer(2),
signalfd(2),
timer_create(2),
timer_gettime(2),
timer_settime(2),
timespec(3),
epoll(7),
time(7)
Русский
перевод
этой
страницы
руководства
был сделан
Azamat Hackimov <
[email protected]>, Dmitry Bolkhovskikh
<
[email protected]>, Yuri Kozlov <
[email protected]> и
Иван
Павлов <
[email protected]>
Этот
перевод
является
бесплатной
документацией;
прочитайте
Стандартную
общественную
лицензию GNU
версии 3
или более
позднюю,
чтобы
узнать об
условиях
авторского
права. Мы не
несем
НИКАКОЙ
ОТВЕТСТВЕННОСТИ.
Если вы
обнаружите
ошибки в
переводе
этой
страницы
руководства,
пожалуйста,
отправьте
электронное
письмо на
[email protected]