一、介绍
在前面的文章中,对timerFD的应用进行过几次分析和举例,这次将其总结一下,特别是与刚刚分析过的eventFD整体对比学习一下,会让大家能够更好的掌握相关的技术细节。timerfd通过将定时器抽象为一个文件描述符,打通了定时器处理与其它IO处理的通道。这样,就可以把相关定时器应用到IO多路复用机制中。统一了编程模型,降低了编程的复杂度。
二、timerFD
timerFD内核中的定义如下:
struct timerfd_ctx {
struct hrtimer tmr; //高精度定时器
ktime_t tintv; // 时间间隔
wait_queue_head_t wqh; // 定时器等待队列
uint64_t ticks; // 计数器
int clockid; // 定时器类型
};
这里需要说明的主要是定时器的类型,主要有以下几种:
- CLOCK_REALTIME
系统实时时间,可能会因NTP调整或系统时间修改而跳变。一般应用于绝对时间的场景 - CLOCK_MONOTONIC
单调递增时钟,不受系统时间调整影响,不过在系统挂起时会停止计时,大多数的时钟应用如时间间隔和超时等都可以应用 - CLOCK_BOOTTIME
单调递增时钟,但在系统挂起期间也会持续计时。应用也很清楚,即需要将系统休眠时间也计算在内的定时任务。 - CLOCK_REALTIME_ALARM
类似CLOCK_REALTIME,但可以在系统挂起时可以唤醒系统(当然可能需要相关权限),它一般用于闹钟类应用
三、计时的应用
无论是在Windows平台还是Linux平台,都有自己的定时器接口,比如Windows平台的SetTimer,CreateTimerQueueTimer等;Linux平台有setitimer,timer_create等。其中Linux平台中,还包括本文提到的timerFD。其工作原理主要分为以下几步:
-
创建定时器上下文
开发者可以调用timerfd_create()接口在内核中创建timerfd_ctx的实例并初始化 -
启动定时器
然后使用timerfd_settime()可以设置第一次到期时间和超时间隔,内核会将设置的参数应用到hrtimer -
超时回调
当定时器对象的的高精度定时器hrtimer到期时,内核会调用回调函数并处理计数器,同时唤醒在等待队列上的阻塞的进程 -
用户接口调用读取
最后,就可以通过多路复用的read等接口(epoll等)来获取定时器的ticks,从而提供给上层展开相关的应用
四、特点
既然有那么多的定时器为什么还要创造一个timerFD的呢?主要原因就是其有着很多特点:
- 统一了事件模型,将事件模型的应用完全抽象
- 提供了高精度的定时器,支持纳秒级别的定时器精度
- 避免了信号竞争,即与传统的信号定时器相比,其通过文件描述符进行事件触发,避免了信号处理的复杂性
- 线程安全,天然支持多线程
当然,有优势必然有不足的地方,主要包括:
-
文件资源句柄的限制
timerfd是一个文件描述符,如果大规模的使用定时器,特别是再和网络句柄等混合应用时,可能就会受到整体资源影响 -
创建的开销较大
由于timerfd的创建和处理都需要和内核打交道,所以其耗费的时间周期相对较长,比上层应用的定时器要慢至少一个量级
五、例程
虽然在前面分析timerFD给出过几个例程,但此处再给一个简单的例程供大家借鉴分析:
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
int main() {
int tfd = timerfd_create(CLOCK_MONOTONIC, 0);
if (tfd == -1) {
printf("timerfd_create err!\n");
return -1;
}
struct itimerspec setValue;
setValue.it_value.tv_sec = 1;
setValue.it_value.tv_nsec = 0;
setValue.it_interval.tv_sec = 1;
setValue.it_interval.tv_nsec = 0;
if (timerfd_settime(tfd, 0, &setValue, NULL) == -1) {
printf("timerfd_settime err!\n");
close(tfd);
return -1;
}
while (1) {
uint64_t expir;
ssize_t s = read(tfd, &expir, sizeof(expir));
if (s != sizeof(expir)) {
printf("read tfd err!\n");
break;
}
printf("timer callback : %lu\n", expir);
}
close(tfd);
return 0;
}
其实这种timerFD的应用,越是简单越容易理解其的应用。再配合前面的相关多路复用的例程就可以顺利的将其应用到相关实际工程中了。
六、总结
通过上面的分析可以知道了timerfd的特点和相关的应用场景,开发者就需要根据自己的实际需求来确定是否引入其进行开发。正所谓好与坏,没有必然的判断条件,哪个更适合于当前的应用,哪个就更好。当然,这个“当前”是动态发展并不断变化的。

1332

被折叠的 条评论
为什么被折叠?



