以下内容描述的都是linux内核下的知识点
什么是linux内核定时器?
内核定时器是内核用来控制在未来某个时间点(基于jiffies)调度执行某个函数的一种机制,其实现位于 <linux/timer.h> 和 kernel/timer.c 文件中。
被调度的函数肯定是异步执行的,它类似于一种“软件中断”,而且是处于非进程的上下文中,所以调度函数必须遵守以下规则:
1) 没有 current 指针、不允许访问用户空间。因为没有进程上下文,相关代码和被中断的进程没有任何联系。
2) 不能执行休眠(或可能引起休眠的函数)和调度。
3) 任何被访问的数据结构都应该针对并发访问进行保护,以防止竞争条件。
内核定时器的调度函数运行过一次后就不会再被运行了(相当于自动注销),但可以通过在被调度的函数中重新调度自己来周期运行。
在SMP系统中,调度函数总是在注册它的同一CPU上运行,以尽可能获得缓存的局域性。
什么是hrtimer?
在聊高精度定时器(hrtimer)之前,我们先聊一下普通软件定时器(timer)。
普通软件定时器,也称作低分辨率定时器,所谓低分辨率定时器,是指这种定时器的计时单位基于jiffies值的计数,也就是说,它的精度只有1/HZ,假如你的内核配置的HZ是1000,那意味着系统中的低分辨率定时器的精度就是1ms。
用途:低分辨率定时器几乎是为“超时”而设计的,并为此对它进行了大量的优化,对于这些以“超时”未目的而使用定时器,它们大多数期望在超时到来之前获得正确的结果,然后删除定时器,精确时间并不是它们主要的目的,例如网络通信、设备IO等等。
简单的低精度定时器使用例子:
1
2
3
4
5
6
7
8
9struct timer_list timer; ...... init_timer(&timer); timer.function = _function; timer.expires = _expires; timer.data = _data; add_timer(&timer); mod_timer(&timer, jiffies+50); del_timer(&timer);
高精度定时器,随着内核的不断演进,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件。内核从2.6.16开始加入了高精度定时器架构。高精度定时器可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,例如多媒体应用,音频设备的驱动程序等等。
hrtimer如何运转?
hrtimer的实现需要一定的硬件基础,依赖于timekeeper和clock_event_device。
hrtimer系统需要通过timekeeper获取当前的时间,计算与到期时间的差值,并根据该差值,设定该cpu的tick_device(clock_event_device)的下一次的到期时间,时间一到,在clock_event_device的事件回调函数中处理到期的hrtimer。
1
2
3
4
5
6
7
8
9
10
11
12
13void hrtimer_init(struct hrtimer *timer, clockid_t which_clock, enum hrtimer_mode mode); int hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode); nt hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, unsigned long range_ns, const enum hrtimer_mode mode); int hrtimer_cancel(struct hrtimer *timer); u64 hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval); static inline u64 hrtimer_forward_now(struct hrtimer *timer,ktime_t interval)
hrtimer的到期处理
高精度定时器系统有3个入口可以对到期定时器进行处理,它们分别是:
没有切换到高精度模式时,在每个jiffie的tick事件中断中进行查询和处理;
在HRTIMER_SOFTIRQ软中断中进行查询和处理;
切换到高精度模式后,在每个clock_event_device的到期事件中断中进行查询和处理;
如何切换到高精度模式?
1
2
3
4
5
6hrtimer_run_pending tick_check_oneshot_change ts->check_clocks ts->nohz_mode hrtimer_is_hres_enabled hrtimer_switch_to_hres
hrtimer的原理是什么?如何实现高精度定时?
以硬件定时器为基础,设计一套hrtimer架构,每一个内核都有自己的hrtimer,各自维护。
原理:高精度定时器通过timekeeper获取当前的时间,计算与到期时间的差值,并根据该差值,设定该cpu的tick_device(clock_event_device)的下一次的到期时间,时间一到,在clock_event_device的事件回调函数中处理到期的hrtimer。
如何实现:理解了高精度时间的原理后,就可以理解如何实现了,首先肯定是需要硬件支持,然后需要内核配置hrtimer,然后添加高精度定时器。并不一定能实现高精度。
内核hrtimer的配置?
1
2
3CONFIG_RTC_HCTOSYS CONFIG_NO_HZ CONFIG_HIGH_RES_TIMERS
hrTimer软件架构
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
49
50struct hrtimer { struct timerqueue_node node; ktime_t _softexpires; enum hrtimer_restart (*function)(struct hrtimer *); struct hrtimer_clock_base *base; unsigned long state; ...... }; 定时器的到期时间用ktime_t来表示,_softexpires字段记录了时间,定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活: enum hrtimer_restart { HRTIMER_NORESTART, /* Timer is not restarted */ HRTIMER_RESTART, /* Timer must be restarted */ }; state字段用于表示hrtimer当前的状态,有几下几种位组合: #define HRTIMER_STATE_INACTIVE 0x00 // 定时器未激活 #define HRTIMER_STATE_ENQUEUED 0x01 // 定时器已经被排入红黑树中 #define HRTIMER_STATE_CALLBACK 0x02 // 定时器的回调函数正在被调用 #define HRTIMER_STATE_MIGRATE 0x04 // 定时器正在CPU之间做迁移 hrtimer的到期时间可以基于以下几种时间基准系统: enum hrtimer_base_type { HRTIMER_BASE_MONOTONIC, // 单调递增的monotonic时间,不包含休眠时间 HRTIMER_BASE_REALTIME, // 平常使用的墙上真实时间 HRTIMER_BASE_BOOTTIME, // 单调递增的boottime,包含休眠时间 HRTIMER_MAX_CLOCK_BASES, // 用于后续数组的定义 }; 和低分辨率定时器一样,处于效率和上锁的考虑,每个cpu单独管理属于自己的hrtimer,为此,专门定义了一个结构hrtimer_cpu_base: struct hrtimer_cpu_base { ...... struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; }; 其中,clock_base数组为每种时间基准系统都定义了一个hrtimer_clock_base结构,它的定义如下: struct hrtimer_clock_base { struct hrtimer_cpu_base *cpu_base; // 指向所属cpu的hrtimer_cpu_base结构 ...... struct timerqueue_head active; // 红黑树,包含了所有使用该时间基准系统的hrtimer ktime_t resolution; // 时间基准系统的分辨率 ktime_t (*get_time)(void); // 获取该基准系统的时间函数 ktime_t softirq_time;// 当用jiffies ktime_t offset; // }; active字段是一个timerqueue_head结构,它实际上是对rbtree的进一步封装: struct timerqueue_node { struct rb_node node; // 红黑树的节点 ktime_t expires; // 该节点代表队hrtimer的到期时间,与hrtimer结构中的_softexpires稍有不同 }; struct timerqueue_head { struct rb_root head; // 红黑树的根节点 struct timerqueue_node *next; // 该红黑树中最早到期的节点,也就是最左下的节点 }; timerqueue_head结构在红黑树的基础上,增加了一个next字段,用于保存树中最先到期的定时器节点,实际上就是树的最左下方的节点,有了next字段,当到期事件到来时,系统不必遍历整个红黑树,只要取出next字段对应的节点进行处理即可。timerqueue_node用于表示一个hrtimer节点,它在标准红黑树节点rb_node的基础上增加了expires字段,该字段和hrtimer中的_softexpires字段一起,设定了hrtimer的到期时间的一个范围,hrtimer可以在hrtimer._softexpires至timerqueue_node.expires之间的任何时刻到期,我们也称timerqueue_node.expires为硬过期时间(hard),意思很明显:到了此时刻,定时器一定会到期,有了这个范围可以选择,定时器系统可以让范围接近的多个定时器在同一时刻同时到期,这种设计可以降低进程频繁地被hrtimer进行唤醒。经过以上的讨论,我们可以得出以下的图示,它表明了每个cpu上的hrtimer是如何被组织在一起的:
为什么hrtimer使用了红黑树架构?
对于高分辨率定时器,我们期望它们的数据结构至少具备如下调节:(稳定且快速查找能力、快速插入和删除定时器能力、排序功能)
内核的开发者考察了多种数据结构,例如基数树、哈希表等等,最终他们选择了红黑树(rbtree)来组织hrtimer,红黑树已经以库的形式存在于内核中,
并被成功地使用在内存管理子系统和文件系统中,随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,
树的最左边的节点就是最快到期的定时器,内核用一个hrtimer结构来表示一个高精度定时器。
hrtimer如何运转?
高精度定时器系统有3个入口可以对到期定时器进行处理,它们分别是:
没有切换到高精度模式时,在每个jiffie的tick事件中断中进行查询和处理;
在HRTIMER_SOFTIRQ软中断中进行查询和处理;
切换到高精度模式后,在每个clock_event_device的到期事件中断中进行查询和处理;
如何实现让hrtimer定时时间小于1ms?关于hrtimer定时器精度
只要硬件支持,内核支持,就可以实现定时时间小于1ms。否则hrtimer会按照时钟TICK调用高精度定时器。
问题描述:
首先kernel中已经打开了high resolution Timer support,
驱动程序中hetimer初始化,设置hrtimer定时时间为60us,但实际示波器检测发现定时时间为1ms(1000/HZ).
在linux上可通过命令来查看调度器的精度。
1cat /proc/timer_list | grep .resolution
可以尝试在驱动中添加打印(具体查看下可能是什么原因导致的高精度定时器无法使用):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19int tick_check_oneshot_change(int allow_nohz) { struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched); if (!test_and_clear_bit(0, &ts->check_clocks)) return 0; printk("%s(%d) ts->check_clocks=%dn", __func__, __LINE__, ts->check_clocks); if (ts->nohz_mode != NOHZ_MODE_INACTIVE) return 0; printk("%s(%d) ts->nohz_mode=%dn", __func__, __LINE__, ts->nohz_mode); if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available()) return 0; printk("%s(%d)allow_nohz=%dn", __func__, __LINE__, allow_nohz); if (!allow_nohz) return 1; printk("%s(%d)n", __func__, __LINE__); tick_nohz_switch_to_nohz(); return 0; }
最后
以上就是不安柚子最近收集整理的关于操作系统 -- Linux 高精度定时器 Hrtimer的全部内容,更多相关操作系统内容请搜索靠谱客的其他文章。
发表评论 取消回复