本文转载自:http://blog.csdn.net/droidphone/article/details/7975694

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[+]

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. const char *name;
  17. struct list_head list;
  18. int rating;
  19. int (*enable)(struct clocksource *cs);
  20. void (*disable)(struct clocksource *cs);
  21. unsigned long flags;
  22. void (*suspend)(struct clocksource *cs);
  23. void (*resume)(struct clocksource *cs);
  24. /* private: */
  25. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
  26. /* Watchdog related data, used by the framework */
  27. struct list_head wd_list;
  28. cycle_t cs_last;
  29. cycle_t wd_last;
  30. #endif
  31. } ____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. static int __init init_jiffies_clocksource(void)
  11. {
  12. return clocksource_register(&clocksource_jiffies);
  13. }
  14. 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进行时间计数和更新操作。

Linux时间子系统之一:clock source(时钟源)【转】相关推荐

  1. Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)【转】

    转自:http://blog.csdn.net/droidphone/article/details/8112948 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 数据结构 ...

  2. linux 多核 系统时钟,Linux时间子系统之(十五):clocksource

    Linux时间子系统之(十五):clocksource 作者:linuxer 发布于:2014-12-1 19:03 分类:时间子系统 一.前言 和洋葱一样,软件也是有层次的,内核往往需要对形形色色的 ...

  3. Linux时间子系统之(五):POSIX Clock

    专题文档汇总目录 Notes: 本章主要介绍了若干种类的静态时钟,这些时钟都可以通过k_clock表示,注册到posix_clocks中.这些都是静态时钟,可以分为三大类:各种REALTIME时钟.带 ...

  4. Linux时间子系统之三:时间的维护者:timekeeper

    专题文档汇总目录 Notes: 原文地址:Linux时间子系统之三:时间的维护者:timekeeper 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法 ...

  5. linux 不同用户时间,Linux时间子系统之(一):时间的基本概念

    Linux时间子系统之(一):时间的基本概念 作者:linuxer 发布于:2014-12-23 12:22 分类:时间子系统 本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一. ...

  6. linux 用户空间 timer,Linux时间子系统(十五) clocksource

    一.前言 和洋葱一样,软件也是有层次的,内核往往需要对形形色色的某类型的驱动进行抽象,屏蔽掉其具体的特质,获取该类驱动共同的逻辑,而又根据这些逻辑撰写该类驱动的抽象层.嵌入式系统总是会提供timer的 ...

  7. Linux时间子系统 clocksource(计算mult和shift)

    Linux时间子系统 clocksource 原文地址:https://www.cnblogs.com/alantu2018/p/8448313.html 一.前言 和洋葱一样,软件也是有层次的,内核 ...

  8. Linux时间子系统之(一):时间的基本概念【转】

    本文转载自:http://www.wowotech.net/timer_subsystem/time_concept.html 本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一. ...

  9. Linux时间子系统之Tick层

    所谓Tick设备,也称作滴答设备,就是系统中的一个周期中断设备,其周期间隔由内核编译选项定义. Tick设备在Linux时间子系统中用tick_device结构体表示(代码位于kernel/time/ ...

  10. Linux时间子系统(一) 基本概念

    本文使用Q & A的方式来和大家以前探讨一下时间的基本概念 一.什么是时间? 这个问题实在是太复杂了,我都不知道这是一个物理学.宇宙学.还是热力学异或是哲学问题,我只是想从几个侧面来了解一下时 ...

最新文章

  1. java 只去掉_30行代码带你了解Java的Serializable接口
  2. Mybatis入门学习---创建第一个Mybatis程序
  3. python生成随机密码
  4. gcc和g++的区别
  5. 一步一步教你抓数据——用.net精确提取网站数据的通用方法 [转]
  6. unity深度查找某个子物体和遍历所有子物体方法
  7. 转:Discuz!NT负载均衡解决方案(HA)之---LVS(Linux Virtual Server)
  8. error: failed to push some refs to ‘github.com:English.git‘ hint: Updates w
  9. linux使用中的问题 --- (Unable to establish SSL connection)
  10. Amobea读写分离
  11. 核电厂的“神经中枢”——核电厂数字化仪控系统
  12. 条码打印工具-Bartender的使用
  13. 查看DELL服务器保修期
  14. 永恒之塔4.0单机MYSQL,永恒之塔数据库
  15. 容器监控实践—CAdvisor
  16. windows xp sp3下载
  17. 支付宝生活号开发配置
  18. D语言之路-第1篇 库函数之争
  19. git查看打tag时间_使用git打tag标签/切换到某个tag时期,删除/查看分支
  20. 国内黑客组织及代表性人物

热门文章

  1. FTool多功能开发工具箱
  2. 金融科技视角看全球资管巨头贝莱德的成功之道
  3. SAP ABAP开发规范
  4. Android Open Source Projects 1
  5. 做什么副业挣钱快?2021有哪些挣钱的副业?
  6. golang之文件读写/复制/断点续传
  7. Win XP关机故障
  8. 数据结构与算法_03_分而治之的分治算法
  9. 音频入门: 最全面详细的Mel频谱和MFCC讲解
  10. SSH2框架搭建 和 配置文件详解