没有合适的资源?快使用搜索试试~ 我知道了~
这些文件主要讨论了Linux内核中的时间子系统,包括时钟源(clock source)、时间表示、时间维护者(timekeeper)、定时器引擎(clock_event_device)、低分辨率定时器、高精度定时器(HRTIMER)、动态时钟框架(CONFIG_NO_HZ、tickless)以及用户态定时器的实现方法。下面是对这些内容的简要概述: 时钟源(clock source): 时钟源为Linux内核提供时间基线,通常由硬件实现,如固定频率的计数器。 内核通过时钟源更新实时时间信息(墙上时间)。 时钟源的精度由其驱动时钟频率决定,内核使用clocksource结构来抽象时钟源。 时间表示: 内核使用不同的时间表示方法,如jiffies(基于时钟滴答的计数)、timeval(秒和微秒)、timespec(秒和纳秒)和ktime(通用时间架构)。 时间维护者(timekeeper): timekeeper结构用于组织与时间相关的数据,包括当前时间(xtime)、单调时间(monotonic time)、原始单调时间(raw monotonic time)和启动时间(boot
资源推荐
资源详情
资源评论
1
Linux 时间子系统之一:clock source(时钟源)
clock source 用于为 linux 内核提供一个时间基线,如果你用 linux 的 date 命令获取当前时间,内核会读取
当前的 clock source,转换并返回合适的时间单位给用户空间。在硬件层,它通常实现为一个由固定时钟
频率驱动的计数器,计数器只能单调地增加,直到溢出为止。时钟源是内核计时的基础,系统启动时,内
核通过硬件 RTC 获得当前时间,在这以后,在大多数情况下,内核通过选定的时钟源更新实时时间信息
(墙上时间),而不再读取 RTC 的时间。本节的内核代码树基于 V3.4.10。
/*****************************************************************************************************/
声明:本博内容均由 http://blog.csdn.net/droidphone 原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
1. struct clocksource 结构
内核用一个 clocksource 结构对真实的时钟源进行软件抽象,现在我们从 clock source 的数据结构开始,
它的定义如下:
[cpp] view plaincopy
1. struct clocksource {
2. /*
3. * Hotpath data, fits in a single cache line when the
4. * clocksource itself is cacheline aligned.
5. */
6. cycle_t (*read)(struct clocksource *cs);
7. cycle_t cycle_last;
8. cycle_t mask;
9. u32 mult;
10. u32 shift;
11. u64 max_idle_ns;
12. u32 maxadj;
13. #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
14. struct arch_clocksource_data archdata;
15. #endif
16.
17. const char *name;
18. struct list_head list;
19. int rating;
20. int (*enable)(struct clocksource *cs);
21. void (*disable)(struct clocksource *cs);
22. unsigned long flags;
23. void (*suspend)(struct clocksource *cs);
2
24. void (*resume)(struct clocksource *cs);
25.
26. /* private: */
27. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
28. /* Watchdog related data, used by the framework */
29. struct list_head wd_list;
30. cycle_t cs_last;
31. cycle_t wd_last;
32. #endif
33. } ____cacheline_aligned;
我们只关注 clocksource 中的几个重要的字段。
1.1 rating:时钟源的精度
同一个设备下,可以有多个时钟源,每个时钟源的精度由驱动它的时钟频率决定,比如一个由 10MHz 时
钟驱动的时钟源,他的精度就是 100nS。clocksource 结构中有一个 rating 字段,代表着该时钟源的精度
范围,它的取值范围如下:
� 1--99: 不适合于用作实际的时钟源,只用于启动过程或用于测试;
� 100--199:基本可用,可用作真实的时钟源,但不推荐;
� 200--299:精度较好,可用作真实的时钟源;
� 300--399:很好,精确的时钟源;
� 400--499:理想的时钟源,如有可能就必须选择它作为时钟源;
1.2 read 回调函数
时钟源本身不会产生中断,要获得时钟源的当前计数,只能通过主动调用它的 read 回调函数来获得当前的
计数值,注意这里只能获得计数值,也就是所谓的 cycle,要获得相应的时间,必须要借助 clocksource 的
mult 和 shift 字段进行转换计算。
1.3 mult 和 shift 字段
因为从 clocksource 中读到的值是一个 cycle 计数值,要转换为时间,我们必须要知道驱动 clocksource 的
时钟频率 F,一个简单的计算就可以完成:
t = cycle/F;
可是 clocksource 并没有保存时钟的频率 F,因为使用上面的公式进行计算,需要使用浮点运算,这在内核
中是不允许的,因此,内核使用了另外一个变通的办法,根据时钟的频率和期望的精度,事先计算出两个
辅助常数 mult 和 shift,然后使用以下公式进行 cycle 和 t 的转换:
t = (cycle * mult) >> shift;
只要我们保证:
F = (1 << shift) / mult;
内核内部使用 64 位进行该转换计算:
[cpp] view plaincopy
1. static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)
2. {
3. return ((u64) cycles * mult) >> shift;
4. }
3
从转换精度考虑,mult 的值是越大越好,但是为了计算过程不发生溢出,mult 的值又不能取得过大。为此
内核假设 cycle 计数值被转换后的最大时间值:10 分钟(600 秒),主要的考虑是 CPU 进入 IDLE 状态后,
时间信息不会被更新,只要在 10 分钟内退出 IDLE,clocksource 的 cycle 计数值就可以被正确地转换为相
应的时间,然后系统的时间信息可以被正确地更新。当然最后的结果不一定是 10 分钟,它由
clocksource_max_deferment 进行计算,并保存 max_idle_ns 字段中,tickless 的代码要考虑这个值,以
防止在 NO_HZ 配置环境下,系统保持 IDLE 状态的时间过长。在这样,由 10 分钟这个假设的时间值,我
们可以推算出合适的 mult 和 shift 值。
2. clocksource 的注册和初始化
通常,clocksource 要在初始化阶段通过 clocksource_register_hz 函数通知内核它的工作时钟的频率,调
用的过程如下:
由上图可见,最终大部分工作会转由__clocksource_register_scale 完成,该函数首先完成对 mult 和 shift
值的计算,然后根据 mult 和 shift 值,最终通过 clocksource_max_deferment 获得该 clocksource 可接受
的最大 IDLE 时间,并记录在 clocksource 的 max_idle_ns 字段中。clocksource_enqueue 函数负责按
clocksource 的 rating 的大小,把该 clocksource 按顺序挂在全局链表 clocksource_list 上,rating 值越大,
在链表上的位置越靠前。
每次新的 clocksource 注册进来,都会触发 clocksource_select 函数被调用,它按照 rating 值选择最好的
clocksource,并记录在全局变量 curr_clocksource 中,然后通过 timekeeping_notify 函数通知
timekeeping,当前 clocksource 已经变更,关于 timekeeping,我将会在后续的博文中阐述。
3. clocksource watchdog
4
系统中可能同时会注册对个 clocksource,各个 clocksource 的精度和稳定性各不相同,为了筛选这些注册
的 clocksource,内核启用了一个定时器用于监控这些 clocksource 的性能,定时器的周期设为 0.5 秒:
[cpp] view plaincopy
1. #define WATCHDOG_INTERVAL (HZ >> 1)
2. #define WATCHDOG_THRESHOLD (NSEC_PER_SEC >> 4)
当有新的 clocksource 被注册时,除了会挂在全局链表 clocksource_list 外,还会同时挂在一个 watchdog
链表上:watchdog_list。定时器周期性地(0.5 秒)检查 watchdog_list 上的 clocksource,
WATCHDOG_THRESHOLD 的值定义为 0.0625 秒,如果在 0.5 秒内,clocksource 的偏差大于这个值就
表示这个 clocksource 是不稳定的,定时器的回调函数通过 clocksource_watchdog_kthread 线程标记该
clocksource,并把它的 rate 修改为 0,表示精度极差。
4. 建立 clocksource 的简要过程
在系统的启动阶段,内核注册了一个基于 jiffies 的 clocksource,代码位于 kernel/time/jiffies.c:
[cpp] view plaincopy
1. struct clocksource clocksource_jiffies = {
2. .name = "jiffies",
3. .rating = 1, /* lowest valid rating*/
4. .read = jiffies_read,
5. .mask = 0xffffffff, /*32bits*/
6. .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */
7. .shift = JIFFIES_SHIFT,
8. };
9. ......
10.
11. static int __init init_jiffies_clocksource(void)
12. {
13. return clocksource_register(&clocksource_jiffies);
14. }
15.
16. core_initcall(init_jiffies_clocksource);
它的精度只有 1/HZ 秒,rating 值为 1,如果平台的代码没有提供定制的 clocksource_default_clock 函数,
它将返回该 clocksource:
[cpp] view plaincopy
1. struct clocksource * __init __weak clocksource_default_clock(void)
2. {
3. return &clocksource_jiffies;
5
4. }
然后,在初始化的后段,clocksource 的代码会把全局变量 curr_clocksource 设置为上述的 clocksource:
[cpp] view plaincopy
1. static int __init clocksource_done_booting(void)
2. {
3. ......
4. curr_clocksource = clocksource_default_clock();
5. ......
6. finished_booting = 1;
7. ......
8. clocksource_select();
9. ......
10. return 0;
11. }
12. fs_initcall(clocksource_done_booting);
当然,如果平台级的代码在初始化时也会注册真正的硬件 clocksource,所以经过 clocksource_select()函
数后,curr_clocksource 将会被设为最合适的 clocksource。如果 clocksource_select 函数认为需要切换更
好的时钟源,它会通过 timekeeping_notify 通知 timekeeping 系统,使用新的 clocksource 进行时间计数和
更新操作。
Linux 时间子系统之二:表示时间的单位和结构
人们习惯用于表示时间的方法是:年、月、日、时、分、秒、毫秒、星期等等,但是在内核中,为了软件
逻辑和代码的方便性,它使用了一些不同的时间表示方法,并为这些表示方法定义了相应的变量和数据结
构,本节的内容就是阐述这些表示方法的意义和区别。
/*****************************************************************************************************/
声明:本博内容均由 http://blog.csdn.net/droidphone 原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
1. jiffies
内核用 jiffies 变量记录系统启动以来经过的时钟滴答数,它的声明如下:
[cpp] view plaincopy
剩余67页未读,继续阅读
资源评论
小坚学Linux
- 粉丝: 3916
- 资源: 52
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功