timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。
#include <sys/timerfd.h>
// 返回值:创建定时器文件描述符
int timerfd_create(int clockid, int flags);
clockid
CLOCK_REALTIME :系统范围内的实时时钟
CLOCK_MONOTONIC
flags
0
O_CLOEXEC
O_NONBLOCK
// 用来启动或关闭有fd指定的定时器
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);
第二个参数:struct itimerspec
struct timespec {
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds */
};
struct itimerspec {
struct timespec it_interval; // 之后每隔多长时间超时
struct timespec it_value; // 第一次超时时间
};
// 获得定时器距离下次超时还剩下的事件
int timerfd_gettime(int fd, struct itimerspec *curr_value);
定时器超时事件的到来时,定时器文件描述符的值将会增加,与eventfd一样,可以用read函数读取定时器文件描述符。
最常用的做法是:将定时器文件描述符添加到epoll树中,由epoll监控该描述符事件,当事件到来时,去处理它(具体的代码见下)
#include <stdio.h>
#include <stdlib.h>
#include <sys/timerfd.h>
#include <time.h>
#include<sys/time.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <assert.h>
const int MAXNUM = 20;
int main(int argc, char *argv[])
{
uint64_t exp;
ssize_t s;
int i;
// 1.构建了一个定时器
int timefd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
assert(timefd != -1);
// 第二个参数(new_value)的初始化
struct itimerspec new_value;
// 第一次到期的时间
struct timespec now;
int ret = clock_gettime(CLOCK_REALTIME, &now);//获取时钟时间
new_value.it_value.tv_sec = 5;
new_value.it_value.tv_nsec = now.tv_nsec;
// 之后每次到期的时间间隔
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
// 2.启动定时器
ret = timerfd_settime(timefd, 0, &new_value, NULL);
assert(ret != -1);
printf("timer started\n"); // 定时器开启啦!
// 3.创建epoll树
int epollfd = epoll_create(1);
struct epoll_event events[MAXNUM];
// 4.timefd定时器事件
struct epoll_event ev;
ev.data.fd = timefd;
ev.events = EPOLLIN | EPOLLET;
// 5.将定时器事件添加到epoll树 && 监控
epoll_ctl(epollfd, EPOLL_CTL_ADD, timefd, &ev);
// 6.死循环,等待定时器事件的到来
for (; ;) {
/* 当定时器到时时,定时器文件描述符将会可读,epoll_wait将会返回*/
int num = epoll_wait(epollfd, events, MAXNUM, 0);
assert(num >= 0);
// 处理返回的epoll事件
for (i = 0; i < num; i++) {
if (events[i].events & EPOLLIN) {
//....处理其他事件
// 处理定时器到时事件
if (events[i].data.fd == timefd) {
// 读取
s = read(events[i].data.fd, &exp, sizeof(uint64_t)); //需要读出uint64_t大小, 不然会发生错误
assert(s == sizeof(uint64_t));
printf("here is timer: timefd = %d\n", events[i].data.fd);
}
}
}
}
close(timefd);
close(epollfd);
return 0;
}
代码运行结果
[g@ ~/test]# gcc time.c -o time -lrt
[g@ ~/test]# ./time
timer started
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3
here is timer: s = 3