clock source用于为linux内核提供一个时间基线,如果你用linux的date命令获取当前时间,内核会读取当前的clock source,转换并返回合适的时间单位给用户空间。在硬件层,它通常实现为一个由固定时钟频率驱动的计数器,计数器只能单调地增加,直到溢出为止。时钟源是内核计时的基础,系统启动时,内核通过硬件RTC获得当前时间,在这以后,在大多数情况下,内核通过选定的时钟源更新实时时间信息(墙上时间),而不再读取RTC的时间。本节的内核代码树基于V3.4.10。

/*****************************************************************************************************/

声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!

/*****************************************************************************************************/

1.  struct clocksource结构

内核用一个clocksource结构对真实的时钟源进行软件抽象,现在我们从clock source的数据结构开始,它的定义如下:

struct clocksource {

/*

* Hotpath data, fits in a single cache line when the

* clocksource itself is cacheline aligned.

*/

cycle_t (*read)(struct clocksource *cs);

cycle_t cycle_last;

cycle_t mask;

u32 mult;

u32 shift;

u64 max_idle_ns;

u32 maxadj;

#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA

struct arch_clocksource_data archdata;

#endif

const char *name;

struct list_head list;

int rating;

int (*enable)(struct clocksource *cs);

void (*disable)(struct clocksource *cs);

unsigned long flags;

void (*suspend)(struct clocksource *cs);

void (*resume)(struct clocksource *cs);

/* private: */

#ifdef CONFIG_CLOCKSOURCE_WATCHDOG

/* Watchdog related data, used by the framework */

struct list_head wd_list;

cycle_t cs_last;

cycle_t wd_last;

#endif

} ____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位进行该转换计算:

static inline s64 clocksource_cyc2ns(cycle_t cycles, u32 mult, u32 shift)

{

return ((u64) cycles * mult) >> shift;

}

从转换精度考虑,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秒:

#define WATCHDOG_INTERVAL (HZ >> 1)

#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:

struct clocksource clocksource_jiffies = {

.name       = "jiffies",

.rating     = 1, /* lowest valid rating*/

.read       = jiffies_read,

.mask       = 0xffffffff, /*32bits*/

.mult       = NSEC_PER_JIFFY <

.shift      = JIFFIES_SHIFT,

};

......

static int __init init_jiffies_clocksource(void)

{

return clocksource_register(&clocksource_jiffies);

}

core_initcall(init_jiffies_clocksource);

它的精度只有1/HZ秒,rating值为1,如果平台的代码没有提供定制的clocksource_default_clock函数,它将返回该clocksource:

struct clocksource * __init __weak clocksource_default_clock(void)

{

return &clocksource_jiffies;

}

然后,在初始化的后段,clocksource的代码会把全局变量curr_clocksource设置为上述的clocksource:

static int __init clocksource_done_booting(void)

{

......

curr_clocksource = clocksource_default_clock();

......

finished_booting = 1;

......

clocksource_select();

......

return 0;

}

fs_initcall(clocksource_done_booting);

当然,如果平台级的代码在初始化时也会注册真正的硬件clocksource,所以经过clocksource_select()函数后,curr_clocksource将会被设为最合适的clocksource。如果clocksource_select函数认为需要切换更好的时钟源,它会通过timekeeping_notify通知timekeeping系统,使用新的clocksource进行时间计数和更新操作。

linux下编写时钟代码,Linux时间子系统之一:clock source(时钟源)【转】(示例代码)...相关推荐

  1. 在Linux下编写Daemon(Linux启动流程2)

    在Linux(以Redhat Linux Enterprise Edition 5.3为例)下,有时需要编写Service.Service也是程序,一般随系统启动用户不干预就不退出的程序,可以称为Se ...

  2. Linux运行911,韦东山-Linux下编写GT911触摸驱动 - 百问网嵌入式问答社区

    源码在最下面 问题一:资源获取Gt911数据手册 在韦老师给的资料里,路径为\06_Datasheet\Extend_modules\7寸LCD模块\电容触控芯片GT911 Datasheet_121 ...

  3. 专业的LaTeX: 在Linux下编写高质量的文档

    专业的LaTeX: 在Linux下编写高质量的文档 Linux下的OpenOffice.KWord等字处理软件虽然在功能上与Microsoft Word类似,但目前在易用性和可用性方面仍然存在许多不足 ...

  4. Linux下编写贪吃蛇游戏

    Linux下编写贪吃蛇游戏 文章目录 Linux下编写贪吃蛇游戏 前言 一.贪吃蛇代码 二.运行贪吃蛇代码 前言 本程序需要ncurses库,ubuntu下安装ncurses可以执行下面命令: sud ...

  5. linux应用程序的编写实验原理,操作系统实验 1.在linux下编写一个应用程序 联合开发网 - pudn.com...

    操作系统实验 所属分类:Linux/Unix编程 开发工具:C/C++ 文件大小:1KB 下载次数:3 上传日期:2019-05-01 20:34:21 上 传 者:烟雨南风起 说明:  1.在lin ...

  6. texlive - 专业的LaTeX: 在Linux下编写高质量的文档

    dnf install texlive* dnf install texworks dnf install latex* http://www.ibm.com/developerworks/cn/li ...

  7. 在linux下编写动态链接库的步骤: g++ -lc

    引用自http://blog.csdn.net/lwhsyit/article/details/2828306 类似Windows系统中的动态链接库,Linux中也有相应的共享库用以支持代码的复用.W ...

  8. 在Linux下编写Daemon

    在Linux下编写Daemon 转自:http://blog.163.com/prevBlogPerma.do?host=manyhappy163&srl=164476831201071811 ...

  9. Linux下date命令的修改时间问题

    Linux下date命令的修改时间问题 最近好想学linux,刚好公司也有一台旧的电脑,可是有次公司突然停电了,我用date命令时出问题了,电脑的时间不对了,我后来到网上搜索了好久,也没找到同时修改时 ...

  10. Linux下文件的三个时间:ctime、mtime、atime的区别

    Linux下文件的三个时间参数 (1)modification time(mtime):内容修改时间 这里的修改时间指的是文件的内容发生变化,而更新的时间. Eg:vi后保存文件. (2)status ...

最新文章

  1. extjs关于jsonreader
  2. Springboot中优雅进行字段校验
  3. java使用ireport生成报表_JasperReport(3)——Java简单使用IReport生成的文件建立报表...
  4. 通过Mesos、Docker和Go,使用300行代码创建一个分布式系统
  5. docker笔记1_安装及常用命令
  6. go interface 的坑
  7. Linux(CentOS 6.7)下配置Mono和Jexus并且部署ASP.NET MVC3、4、5和WebApi(跨平台)
  8. UI自动化之读取浏览器配置
  9. 单片机的 FIFO循环队列实现
  10. [综述泛读] A survey on web services composition (IJWGS, 2005)
  11. electron forge 好用吗_在优麒麟上使用 Electron 开发桌面应用
  12. 【重难点】【JUC 03】怎么实现一个线程安全的队列、手写模拟实现一个阻塞队列
  13. DevOps - 用 Ansible 管理 VMware 虚拟机
  14. 8. 工厂设计模式(factory pattern)
  15. Oracle数据库索引使用及索引失效总结
  16. Java并发环境下,先操作数据库还是先操作缓存?
  17. vijos 1471 线性DP+贪心
  18. 百度飞桨—— 车牌识别学习与修改
  19. 高德地图--水波雷达动画
  20. OSChina 周三乱弹 —— 总觉得路过是 VIVO 大酒店

热门文章

  1. Javaweb中利用kaptcha生成验证码
  2. Mysql-Proxy自定义守护进程
  3. 关于CS架构文件传输流的问题,文中代码都是转自网上,但可保证代码无无误...
  4. reactjs基础知识:原生事件绑定
  5. 从 Ubuntu 21.04 升级到 Ubuntu 21.10
  6. Scala 闭包详解
  7. maven快照版本机制详解
  8. kafka架构组件概念详解:Broker、Topic、Partition、Leader/Follower、Consumer Group、zookeeper
  9. Spring Boot自定义缓存注解
  10. GridFS读文件代码示例