Linux时间子系统之一:clock source(时钟源)

来源:互联网 发布:java常见异常 编辑:程序博客网 时间:2024/05/16 15:56

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);  
  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. }  
从转换精度考虑,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

系统中可能同时会注册对个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;  
  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进行时间计数和更新操作。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 想注册个公司要怎么办 域名续费不知道找谁怎么办 代收快递弄丢了怎么办 货到付款的快递人不在怎么办 快递送货上门人不在怎么办 ems快递签收人不在怎么办 快递被别人取了怎么办 怎么办快递宗和收发点 快递电话写错了怎么办 网上买沙发想退货怎么办 买的电脑想退货怎么办 买了衣服想退货怎么办 天猫买药审核通过后不要了怎么办 京东维修无发票怎么办 京东维修没有发票怎么办 苹果6s外音没了怎么办 苹果6splus开不了机怎么办 顺丰快递寄件填错收件人地址怎么办 收快递电话换了怎么办 顺丰快递没人收怎么办 网购东西没收到怎么办 中通快递没收到怎么办 快递员不给验货怎么办 顺丰验货不要了怎么办 闲鱼买家掉包了怎么办 闲鱼正在退款中怎么办 拒收货物卖家拒绝退款怎么办 货物没问题淘宝卖家拒收怎么办 头发稀少长的慢怎么办 没满16岁怎么办银行卡 网上买东西手机号填错了怎么办 买东西电话号码填错了怎么办 淘宝联盟扣54分怎么办 联盟被扣54分怎么办 ofo押金退了余额怎么办 网购还没收货就已签收怎么办 理财公司倒闭分公司法人怎么办 公司让离职不想走怎么办 公司让离职自己不想走怎么办 小孩子有购物狂病怎么办 拉杆箱的轮子卡怎么办