我是靠谱客的博主 不安柚子,这篇文章主要介绍操作系统 -- Linux 高精度定时器 Hrtimer,现在分享给大家,希望可以做个参考。

以下内容描述的都是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
9
struct 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
13
void 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
6
hrtimer_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
3
CONFIG_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
50
struct 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上可通过命令来查看调度器的精度。

复制代码
1
cat /proc/timer_list | grep .resolution

可以尝试在驱动中添加打印(具体查看下可能是什么原因导致的高精度定时器无法使用):

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int 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的全部内容,更多相关操作系统内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(77)

评论列表共有 0 条评论

立即
投稿
返回
顶部