timerfd 介绍
timerfd 是在Linux内核2.6.25版本中添加的接口,其是Linux为用户提供的一个定时器接口。这个接口基于文件描述符,所以可以被用于select/poll/epoll的场景。当使用timerfd API创建多个定时器任务并置于poll中进行事件监听,当没有可响应的事件,则程序阻塞在poll中,当有事件发生,通过poll的这个事件入口,对产生的事件进行响应,从而构成了一个事件轮训程序。
timerfd 相关函数
#include <time.h>
int clock_gettime(clockid_t clockid, struct timespec *tp);
- 1
- 2
- 3
clock_gettime函数主要用于获取系统时间,精确到纳秒级别。在编译时需要添加-lrt库,clockid_t clockid指定用何种模式获取时间,struct timespec *tp用于存储获取到的时间。其中clockid主要有如下常用的参数:
CLOCK_REALTIME:系统实时时间,随系统实时时间改变而改变,即从UTC1970-1-1 0:0:0开始计时,中间时刻如果系统时间被用户改成其他,则对应的时间相应改变
CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
CLOCK_PROCESS_CPUTIME_ID:本进程到当前代码系统CPU花费的时间
CLOCK_THREAD_CPUTIME_ID:本线程到当前代码系统CPU花费的时间
#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);
- 1
- 2
- 3
- 4
- 5
- timerfd_create函数主要用于生成一个定时器对象,返回与之关联的文件描述符,clockid可以设置CLOCK_REALTIME和CLOCK_MONOTONIC,flags可以设置为TFD_NONBLOCK(非阻塞),TFD_CLOEXEC(同O_CLOEXEC)
- timerfd_settime用于启动和停止定时器,fd为timerfd_create获得的定时器文件描述符,flags为0表示是相对定时器,为TFD_TIMER_ABSTIME表示是绝对定时器。const struct itimerspec *new_value表示设置超时的时间。
其数据结构:
struct timespec {time_t tv_sec; /* Seconds */long tv_nsec; /* Nanoseconds */};struct itimerspec {struct timespec it_interval; /* Interval for periodic timer */struct timespec it_value; /* Initial expiration */};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
需要注意的是itimerspec 结构成员表示的意义:
it_value是首次超时时间,需要填写从clock_gettime获取的时间,并加上要超时的时间。 it_interval是后续周期性超时时间,是多少时间就填写多少。
it_interval不为0则表示是周期性定时器。
it_value和it_interval都为0表示停止定时器。
- timerfd_gettime此函数用于获得定时器距离下次超时还剩下的时间。如果调用时定时器已经到期,并且该定时器处于循环模式(设置超时时间时struct itimerspec::it_interval不为0),那么调用此函数之后定时器重新开始计时。
参考示例
示例一
int tu_set_timer(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{int fd;struct itimerspec its;//创建的定时器 fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);if (fd == -1){LOG_PRINT("Error creating timer");return -1;}//设置超时时间its.it_value.tv_sec = (milliseconds * 1000000) / 1000000000;its.it_value.tv_nsec = (milliseconds * 1000000) % 1000000000;//如果是周期定时器,则设置it_interval,如果不是则为0its.it_interval.tv_sec = continious ? its.it_value.tv_sec : 0;its.it_interval.tv_nsec = continious ? its.it_value.tv_nsec : 0;//设置定时到达后的响应函数及其函数参数timer->timer_handler_cb = timer_handler_cb;timer->timer_handler_arg = timer_handler_arg;timer->continious = continious;//标记是否为循环周期定时器//启动定时器,并将文件描述符添加到poll中监听if ((timerfd_settime(fd, 0, &its, NULL) == 0) && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1)){timer->in_use = true;return 0;}close(fd);return -1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
示例二
int tu_set_timer_realtime(tu_timer_t * timer, uint64_t milliseconds, bool continious, timer_handler_cb_t timer_handler_cb, void * timer_handler_arg)
{int fd;struct itimerspec its;struct timespec now;time_t tv_sec;long tv_nsec;//获取绝对时间if(clock_gettime(CLOCK_REALTIME,&now) == -1){LOG_PRINT("Error clock_gettime timer\n");return -1;}//创建定时器,非阻塞方式 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);if (fd == -1){LOG_PRINT("Error creating timer\n");return -1;}//计算时间tv_sec = (milliseconds * 1000000) / 1000000000;tv_nsec = (milliseconds * 1000000) % 1000000000;//设置到期时间its.it_value.tv_sec = now.tv_sec + tv_sec;its.it_value.tv_nsec = now.tv_nsec + tv_nsec;//如果使用循环模式,设置循环间隔its.it_interval.tv_sec = continious ? tv_sec : 0;its.it_interval.tv_nsec = continious ? tv_nsec : 0;//设置定时到达后的响应函数及其函数参数timer->timer_handler_cb = timer_handler_cb;timer->timer_handler_arg = timer_handler_arg;timer->continious = continious;//启动定时器,并将文件描述符添加到poll中监听if ((timerfd_settime(fd,TFD_TIMER_ABSTIME, &its, NULL) == 0) && ((timer->fd_index = polling_define_poll_fd(fd, POLLIN, tu_timer_handler, timer)) != -1)){timer->in_use = true;LOG_PRINT("tu_set_timer_realtime--\n");return 0;}LOG_PRINT("Error setting timer\n");close(fd);return -1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
poll中的回调函数
void tu_timer_handler(void * arg)
{tu_timer_t * timer = arg;uint64_t exp;if (timer->continious)//重复定时器{if (read(polling_fds[timer->fd_index].fd, &exp, sizeof(uint64_t)) != sizeof(uint64_t)){LOG_PRINT("%p ERROR timer read. Killing timer.\n", timer);tu_kill_timer(timer);}}else{tu_kill_timer(timer);//关闭定时器}//调用定时器处理函数timer->timer_handler_cb(timer->timer_handler_arg);
}