- bullseye-backports 4.18.0-1~bpo11+1
- testing 4.18.0-1
- unstable 4.18.0-1
TIMERFD_CREATE(2) | Руководство программиста Linux | TIMERFD_CREATE(2) |
ИМЯ¶
timerfd_create, timerfd_settime, timerfd_gettime - таймеры, уведомляющие через файловые дескрипторы
СИНТАКСИС¶
#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 *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()¶
Вызов 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()¶
Вызов timerfd_settime() запускает или останавливает таймер, на который ссылается файловый дескриптор fd.
В аргументе new_value задаётся начальное срабатывание и интервал таймера. Для этого аргумента используется структура itimerspec, содержащая два поля, каждое из которых, в свою очередь, является структурой timespec:
struct timespec {
time_t tv_sec; /* секунды */
long tv_nsec; /* наносекунды */ }; struct itimerspec {
struct timespec it_interval; /* интервал для периодического
таймера */
struct timespec it_value; /* первое срабатывание */ };
В 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()¶
Вызов 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)
- Если таймер уже сработал один или более раз с момента настройки с помощью timerfd_settime(), или после последнего успешного read(2), то в буфер, указанный в read(2), будет возвращено беззнаковое 8-байтное целое (uint64_t), содержащее количество произошедших срабатываний (возвращаемое значение хранится в порядке байт узла, то есть родном порядке для целых чисел машины выполнения).
- Если таймер ещё не срабатывал до вызова 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)¶
После fork(2) потомки наследуют копию файлового дескриптора, созданного timerfd_create(). Файловый дескриптор потомка ссылается на тот же объект таймера, что и файловый дескриптор его родителя, и операция read(2) в потомке будет возвращать информацию о срабатываниях таймера.
Поведение при execve(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() также может завершиться со следующими ошибками:
ВЕРСИИ¶
Данные системные вызовы доступны в Linux начиная с ядра версии 2.6.25. Поддержка в библиотеке glibc появилась в версии 2.8.
СООТВЕТСТВИЕ СТАНДАРТАМ¶
Данные системные вызовы есть только в Linux.
ЗАМЕЧАНИЯ¶
Suppose the following scenario for CLOCK_REALTIME or CLOCK_REALTIME_ALARM timer that was created with timerfd_create():
- (а)
- The timer has been started (timerfd_settime()) with the TFD_TIMER_ABSTIME and TFD_TIMER_CANCEL_ON_SET flags;
- (б)
- A discontinuous change (e.g., settimeofday(2)) is subsequently made to the CLOCK_REALTIME clock; and
- (в)
- 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: таймер запущен 3.000: read: 1; всего=1 4.000: read: 1; всего=2 ^Z # нажато control-Z для приостанова программы [1]+ Stopped ./timerfd3_demo 3 1 100 $ fg # возобновление выполнения после нескольких
#секунд a.out 3 1 100 9.660: read: 5; всего=7 10.000: read: 1; всего=8 11.000: read: 1; всего=9 ^C # нажато control-C для приостанова программы
Исходный код программы¶
#include <sys/timerfd.h> #include <time.h> #include <unistd.h> #include <inttypes.h> /* определение PRIu64 */ #include <stdlib.h> #include <stdio.h> #include <stdint.h> /* определение of uint64_t */ #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) static void print_elapsed_time(void) {
static struct timespec start;
struct timespec curr;
static int first_call = 1;
int secs, nsecs;
if (first_call) {
first_call = 0;
if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
handle_error("clock_gettime");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
handle_error("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[]) {
struct itimerspec new_value;
int max_exp, fd;
struct timespec now;
uint64_t exp, tot_exp;
ssize_t s;
if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s нач-сек [интервал макс-сраб]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
handle_error("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)
handle_error("timerfd_create");
if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
handle_error("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))
handle_error("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), epoll(7), time(7)
ЗАМЕЧАНИЯ¶
Эта страница является частью проекта Linux man-pages версии 5.10. Описание проекта, информацию об ошибках и последнюю версию этой страницы можно найти по адресу https://www.kernel.org/doc/man-pages/.
ПЕРЕВОД¶
Русский перевод этой страницы руководства был сделан Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitry Bolkhovskikh <d20052005@yandex.ru>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>
Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.
Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.
13 августа 2020 г. | Linux |