timerfd 是 Linux 为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,因此可以配合 select/poll/epoll 等使用。
timerfd_create 创建定时器对象函数
头文件:
#include <sys/timerfd.h>
函数原型:
int timerfd_create(int clockid, int flags);
作用:
创建一个定时器对象,同时返回一个与之关联的文件描述符
参数含义:
clockid: 标识指定的时钟计数器,可选值 CLOCK_REALTIME、CLOCK_MONOTONIC…
CLOCK_REALTIME:
系统实时时间
,
随系统实时时间改变而改变
,
即从
UTC1970-1-1 0:0:0 开始计时
,
中间时刻如果系统时间被用户改成其他
,
则对应的时间相应改变
CLOCK_MONOTONIC
:
从系统启动这一刻起开始计时
,
不受系统时间被用户改变的影
响
flags:可以为 0 或 TFD_NONBLOCK,TFD_CLOEXEC。0 表示无属性,TFD_NONBLOCK表示设置为非阻塞模式,TFD_CLOEXEC
表示当程序执行
exec
函数时本
fd
将被系统自动关闭
,
表示不传递.
在
2.6.26
之前的
Linux
版本中,标志必须指定为零。
返回值:
如果成功返回一个定时器的文件描述符,失败返回-1
timerfd_settime 设置新的超时时间函数
头文件:
#include <sys/timerfd.h>
函数原型:
int timerfd_settime(int fd, int flags,const struct itimerspec *new_value,struct itimerspec *old_value);
作用:
此函数用于设置新的超时时间,并开始计时,
能够启动和停止定时器
;
参数含义:
fd:
timerfd_create
函数返回的文件句柄
flags:
设置为
1
代表设置的是绝对时间;为
0
代表相对时间。
new_value:
参数
new_value
指定定时器的超时时间以及超时间隔时间
,
new_value.it_value 非零则启动定时器,否则关闭定时器,如果
new_value.it_interval
为0,则定时器只定时一次,即初始那次,否则之后每隔设定时间超时一次
old_value:
不为
null
,则返回定时器这次设置之前的超时时间
struct itimerspec
{
struct timespec it_interval; //定时间隔周期
struct timespec it_value; //第一次超时时间
};
struct timespec
{
__time_t tv_sec; /* 秒. */
__syscall_slong_t tv_nsec; /* 纳秒. */
};
返回值:
成功返回 0,失败返回-1
timerfd_gettime 获取距离下次超时的剩余时间
头文件:
#include <sys/timerfd.h>
函数原型:
int timerfd_gettime(int fd, struct itimerspec *curr_value);
作用:
获取距离下次超时剩余的时间
参数含义:
fd:
timerfd_create
函数返回的文件句柄
curr_value:
curr_value.it_value
字段表示距离下次超时的时间,如果该值为
0
,表示计时器已经解除,
改字段表示的值永远是一个相对值,无论
TFD_TIMER_ABSTIME
是否被设置. curr_value.it_interval
定时器间隔时间
返回值:
成功返回 0,失败返回-1
read
读取
timefd
超时事件通知
当定时器超时,read
读事件发生即可读,返回超时次数(从上次调用timerfd_settime()启动开始或上次
read
成功读取开始),它是一个
8
字节的
unit64_t
类型整数,如果定时器没有发生超时事件,则 read
将阻塞若
timerfd
为阻塞模式,否则返回EAGAIN 错误(
O_NONBLOCK
模式),如果
read
时提供的缓冲区小于
8
字节将以EINVAL 错误返回。
示例程序
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <unistd.h>
#define EPOLL_LISTEN_CNT 256
#define EPOLL_LISTEN_TIMEOUT 500
static int g_epollfd = -1;
static int g_timerfd = -1;
uint64_t tot_exp = 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)
printf("clock_gettime\n");
}
if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
printf("clock_gettime\n");
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);
}
void timerfd_handler(int fd)
{
uint64_t exp = 0;
read(fd, &exp, sizeof(uint64_t));
tot_exp += exp;
print_elapsed_time();
printf("read: %llu, total: %llu\n", (unsigned long long)exp, (unsigned long long)tot_exp);
return;
}
void epoll_event_handle(void)
{
int i = 0;
int fd_cnt = 0;
int sfd;
struct epoll_event events[EPOLL_LISTEN_CNT];
memset(events, 0, sizeof(events));
while(1)
{
/* wait epoll event */
fd_cnt = epoll_wait(g_epollfd, events, EPOLL_LISTEN_CNT,EPOLL_LISTEN_TIMEOUT);
for(i = 0; i < fd_cnt; i++)
{
sfd = events[i].data.fd;
if(events[i].events & EPOLLIN)
{
if (sfd == g_timerfd)
{
timerfd_handler(sfd);
}
}
}
}
}
int epoll_add_fd(int fd)
{
int ret;
struct epoll_event event;
memset(&event, 0, sizeof(event));
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
ret = epoll_ctl(g_epollfd, EPOLL_CTL_ADD, fd, &event);
if(ret < 0)
{
printf("epoll_ctl Add fd:%d error, Error:[%d:%s]\n", fd, errno,strerror(errno));
return -1;
}
printf("epoll add fd:%d--->%d success\n", fd, g_epollfd);
return 0;
}
int epollfd_init()
{
int epfd;
/* create epoll fd */
epfd = epoll_create(EPOLL_LISTEN_CNT);
if (epfd < 0)
{
printf("epoll_create error, Error:[%d:%s]\n", errno,
strerror(errno));
return -1;
}
g_epollfd = epfd;
printf("epoll fd:%d create success\n", epfd);
return epfd;
}
int timerfd_init()
{
int tmfd;
int ret;
struct itimerspec new_value;
new_value.it_value.tv_sec = 2;
new_value.it_value.tv_nsec = 0;
new_value.it_interval.tv_sec = 1;
new_value.it_interval.tv_nsec = 0;
tmfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (tmfd < 0)
{
printf("timerfd_create error, Error:[%d:%s]\n", errno,
strerror(errno));
return -1;
}
ret = timerfd_settime(tmfd, 0, &new_value, NULL);
if (ret < 0)
{
printf("timerfd_settime error, Error:[%d:%s]\n", errno,
strerror(errno));
close(tmfd);
return -1;
}
if (epoll_add_fd(tmfd))
{
close(tmfd);
return -1;
}
g_timerfd = tmfd;
return 0;
}
int main(int argc, char **argv)
{
if (epollfd_init() < 0)
{
return -1;
}
if (timerfd_init())
{
return -1;
}
/* event handle */
epoll_event_handle();
return 0;
}