高精度定时器
在linux驱动模块中使用高精度定时器
1、理论基础
1、高精度定时器:hrtimer(high resolution timer),内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,例如多媒体应用,音频设备的驱动程序等等
2、高精度定时器的储存位置:随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器
3、高精度定时器的表示
1 | struct hrtimer { |
3.1、**_softexpires**:记录了定时器的到期时间
hrtimer的到期时间可以基于以下几种时间基准系统:
1 | enum hrtimer_base_type { |
3.2、hrtimer_restart:定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活
1 | enum hrtimer_restart { |
3.3、state字段用于表示hrtimer当前的状态,有几下几种位组合:
1 |
3.4、处于效率和上锁的考虑,每个cpu单独管理属于自己的hrtimer,为此,专门定义了一个结构hrtimer_cpu_base:
1 | struct hrtimer_cpu_base { |
3.5、其中,clock_base数组为每种时间基准系统都定义了一个hrtimer_clock_base结构,它的定义如下:
1 | struct hrtimer_clock_base { |
3.6、active字段是一个timerqueue_head结构,它实际上是对rbtree的进一步封装:
1 | struct timerqueue_node { |
4、 hrtimer如何运转
hrtimer系统需要通过timekeeper获取当前的时间,计算与到期时间的差值,并根据该差值,设定该cpu的tick_device(clock_event_device)的下一次的到期时间,时间一到,在clock_event_device的事件回调函数中处理到期的hrtimer。一旦开启了hrtimer,tick_device所关联的clock_event_device的事件回调函数会被修改为:hrtimer_interrupt,并且会被设置成工作于CLOCK_EVT_MODE_ONESHOT单触发模式。
5、hrtimer相关API
5.1、hrtimer初始化(常用)
1 | void hrtimer_init(struct hrtimer *timer, //设定回调函数:timer.function = hr_callback; |
5.2、hrtimer激活
5.2.1、可以在设定回调函数后直接使用hrtimer_start激活该定时器(定时器无需指定一个到期范围):
1 | int hrtimer_start(struct hrtimer *timer, ktime_t tim, |
5.2.2、可以使用hrtimer_start_range_ns激活定时器(需要指定到期范围):
1 | hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, |
5.3、hrtimer取消
1 | int hrtimer_cancel(struct hrtimer *timer); |
5.4、用于推后hrtimer的到期时间
1 | extern u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval); |
5.5、获取hrtimer的当前状态
1 | static inline int hrtimer_active(const struct hrtimer *timer) |
6、hrtimer的到期处理
高精度定时器系统有3个入口可以对到期定时器进行处理,它们分别是:
- 没有切换到高精度模式时,在每个jiffie的tick事件中断中进行查询和处理;
- 在HRTIMER_SOFTIRQ软中断中进行查询和处理;
- 切换到高精度模式后,在每个clock_event_device的到期事件中断中进行查询和处理;
7、hrtimer的工作流程
系统在启动的开始阶段,还是按照传统的模式在运行:tick_device按HZ频率定期地产生tick事件,这时的hrtimer工作在低分辨率模式,到期事件在每个tick事件中断中由hrtimer_run_queues函数处理,同时,在低分辨率定时器(时间轮)的软件中断TIMER_SOFTIRQ中,hrtimer_run_pending会被调用,系统在这个函数中判断系统的条件是否满足切换到高精度模式,如果条件满足,则会切换至高分辨率模式,另外提一下,NO_HZ模式也是在该函数中判断并切换
参考链接:Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现_时间子系统之高精度 timer 实现-CSDN博客
2、配置流程
1、hrtimer_init函数初始化定时器工作模式
1 | void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, |
- timer:设定超时回调函数,timer.function = hr_callback;
- which_clock:CLOCK_REALTIME(平常使用的墙上真实时间)、CLOCK_MONOTONIC(单调递增的monotonic时间,不包含休眠时间)、CLOCK_BOOTTIME(单调递增的boottime,包含休眠时间)中的一种(设定时间基准系统)
- mode:HRTIMER_MODE_REL(相对时间)和HRTIMER_MODE_ABS(绝对时间)
2、使用hrtimer_start激活该定时器
1 | int hrtimer_start(struct hrtimer *timer, ktime_t tim, |
根据time和mode参数的值计算hrtimer的超时时间,并设置到timer->expire域。 expire设置的是绝对时间,所以如果参数mode的值为HRTIMER_MODE_REL(即参数tim的值为相对时间),那么需要将tim的值修正为绝对时间:expire = tim + timer->base->get_time(),调用enqueue_hrtimer,将hrtimer加入到红黑树(hrtimer的存储形式)中
3、设置超时时间ktime_set
1 | tim=ktime_set(const long secs, const unsigned long nsecs) |
- 1s【秒】 = 1000ms【毫秒】
- 1ms【毫秒】 = 1000μs【微秒】
- 1μs【微秒】 = 1000ns【纳秒】
4、使用hrtimer_cancel取消一个hrtimer
1 | int hrtimer_cancel(struct hrtimer *timer); |
5、定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活
1 | enum hrtimer_restart { |
6、把hrtimer的到期时间推进一个tick周期,返回HRTIMER_RESTART表明该hrtimer需要再次启动,以便产生下一个tick事件
1 | hrtimer_forward(timer, now, tick_period){ |
参考链接:Linux内核高精度定时器hrtimer 使用实例_struct hrtimer *timer-CSDN博客
3、实例
demo_timer.c代码:
1 |
|
3.1、makefile编写
1 | .PHONY: all clean |
3.2、编译,加载模块
1 | #生成构建文件 |
3.3、实验结果:1s打印一次回调函数中的内容(在var/log/kern.log中查看内容)