man 文档
#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);
These system calls create and operate on a timer that delivers timer expiration notifications via a file descriptor. They provide an alternative to the use of setitimer(2) or timer_create(2), with the advantage that the file descriptor may be monitored by select(2), poll(2), and epoll(7).
The use of these three system calls is analogous to the use of timer_create(2), timer_settime(2), and timer_gettime(2). (There is no analog of timer_getoverrun(2), since that functionality is provided by read(2), as described below.)
使用timerfd可以使用一个file descriptor
上的readable
事件获取定时器的到期通知。这样可以很方便的将定时器与IO multiplexing(select、epoll)结合起来,做到统一事件源
。
Example
man文档自带的例子:
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h> /* Definition 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 init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
handle_error("clock_gettime");
/* Create a CLOCK_REALTIME absolute timer with initial
expiration and interval as specified in command line */
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("timer started\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: %llu; total=%llu\n",
(unsigned long long) exp,
(unsigned long long) tot_exp);
}
exit(EXIT_SUCCESS);
}
编译
$ gcc timerfd.c -o timerfd -lrt -Wall
程序有三个可选参数,参数1是首次超时时间(相对于程序启动时间)、参数2是定时器的周期、参数3是控制定时器的次数
运行
$ ./timerfd 3 1 3
0.000: timer started
3.000: read: 1; total=1
4.000: read: 1; total=2
5.000: read: 1; total=3
struct itimerspec
itimerspec结构体,it_interval
是定时器的周期时间,it_value
是定时器第一次超时触发的时间。
struct itimerspec {
struct timespec it_interval; /* Interval for periodic timer */
struct timespec it_value; /* Initial expiration */
};
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
CLOCK_REALTIME CLOCK_MONOTONIC
clock_gettime
函数可以获取系统时钟,精确到纳秒。需要在编译时指定库:-lrt。可以获取两种类型时间:
CLOCK_REALTIME
:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值。也就是,它以系统时间为坐标。CLOCK_MONOTONIC
:与CLOCK_REALTIME
相反,它是以绝对时间为准,获取的时间为系统重启到现在的时间,更改系统时间对它没有影响。
timerfd_create
clokcid为上面提到的CLOCK_REALTIME或CLOCK_MONOTONIC
,flags为TFD_NONBLOCK TFD_CLOEXEC
的组合。
int timerfd_create(int clockid, int flags);
timerfd_settime
int timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value);
fd通过timerfd_create
获得
flags:
- 0 : 启动一个相对定时器,即相对当前事件的定时器,时间间隔由
new_value->it_value
指定。 TFD_TIMER_ABSTIME
: 启动一个绝对定时器,new_value->it_value
代表的是一个绝对时间。当时钟的值达到new_value->it_value
时超时。
new_value
指定首次超时时机,周期等信息。 old_value
保存原来的设置。
超时时使用read
定时器超时时,对应的timerfd会为readable,这是可以使用read读取到一个
unsigned 8-byte integer (uint64_t)
,它以主机字节序存储了超时次数。
timerfd_settime使用相对时间
int main(int argc, char *argv[])
{
struct itimerspec new_value;
int max_exp, fd;
uint64_t exp, tot_exp;
ssize_t s;
if ((argc != 2) && (argc != 4)) {
fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
argv[0]);
exit(EXIT_FAILURE);
}
new_value.it_value.tv_sec = atoi(argv[1]);
new_value.it_value.tv_nsec = 0;
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, 0, &new_value, NULL) == -1) // flags为0,new_value中是相对时间
handle_error("timerfd_settime");
print_elapsed_time();
printf("timer started\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: %llu; total=%llu\n",
(unsigned long long) exp,
(unsigned long long) tot_exp);
}
exit(EXIT_SUCCESS);
}