Linux中的时钟和定时测量

定时测量

  • 获得当前的时间和日期
  • 维持定时器:用来提醒内核或用户程序某一时间间隔已经过去了

定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的

硬时钟

1、实时时钟RTC

独立于CPU和其他所有芯片,依靠一个独立的小电源供电,即使关闭PC电源,还会继续运转

2、时间戳计数器TSC

  • 有一个CLK输入引线,接收外部振荡器的时钟信号
  • 一个64位的、用作时间戳计数器的寄存器,在每个时钟信号(CLK)到来时+1

与后面介绍的PIT相比,TSC可以获得更精确的时钟
为此,Linux在系统初始化的时候必须确定时钟信号CLK的频率(即CPU的实际频率)

3、可编程间隔定时器PIT

  • 经过适当编程后,可以周期性的给出时钟中断
  • 内核使用的产生时钟中断的设备,产生的时钟中断依赖于硬件的体系结构,慢的为 10 ms 一次,快的为 1 ms 一次
  • High Precision Event Timer ( HPET ):PIT 和 RTC 的替代者,和之前的计时
    器相比,HPET 提供了更高的时钟频率

在init_pit_timer ()中初始化时钟中断频率

Linux的计时体系结构

  • 更新自系统启动以来所经过的时间
  • 更新时间和日期
  • 确定当前进程的执行时间,考虑是否要抢占
  • 更新资源使用统计计数
  • 检查到期的软定时器

定时器是一种软件功能,即允许在将来的某个时刻,函数在给定的时间间隔用完时被调用

在单处理器系统中,所有定时活动都由IRQ0上的时钟中断触发,包括在中断中立即执行的部分,和作为下半部分延迟执行的部分

内核使用两个基本的时间保持函数:一个保持当前最新的时间,另一个计算在当前秒内走过的微秒数。

两个时钟源

  • 定时时钟源:周期性的引起时钟中断,一般为PIT,读取计时时钟源的值更新时间
    定时时钟源周期性的产生中断,使time_interrupt()运行,对下面各项进行更新:
    • 更新自系统启动以来所经过的时间
    • 更新时间和日期
    • 确定当前进程的执行时间,考虑是否要抢占
    • 更新资源使用统计计数
    • 检查到期的软定时器
  • 计时时钟源:表示系统时间

计时体系结构中的数据结构和变量

Linux中的时钟和定时测量
1、计时时钟源
2、Jiffies变量:相对时间,记录从系统启动直到当前时刻的系统时钟产生的滴答数,由系统时钟中断进行维护,用来提醒内核或用户进程一段指定的时间已经过去了

  • 记录系统自启动以来系统产生的tick数
  • 每次时钟中断+1(在系统响应时钟中断,时钟中断处理程序timer_interrupt()将该变量的值 +1 )
  • 因为一秒钟内产生系统时钟中断次数等于宏定义HZ的值,所以变量jiffies的值在一秒内增加HZ

3、Xtime变量:墙上时间,即系统的当前时间

  • 在系统启动过程中根据实时时钟(RTC)芯片保存数据进行初始化;
  • 该变量的值在系统运行过程中由系统时钟中断处理程序负责在每次时钟中断时进行更新,
  • 基本上每个tick更新一次,update_wall_time
  • 墙上时间存储于系统核心变量xtime中,该变量记录了现实世界中的年月日格式的时间,以便内核对某些对象和事件作时间标记,如记录文件的创建时间、修改时间、上次访问时间,或者供用户进程通过系统调用来使用。

注册好的时钟源链表clocksource_list

  • 注册Jiffies时钟源(缺省时钟源):jiffies_read,init_jiffies_clocksource,core_initcall(init_jiffies_clocksource)
  • 注册tsc时钟源:read_tsc,tsc_init
  • 注册pit作为时钟源:init_pit_clocksource,arch_initcall(init_pit_clocksource)

linux中所有被注册的时钟源,按照精度排列,精度最高的头部
Linux中的时钟和定时测量
Linux中的时钟和定时测量

时钟初始化

  • time_init中调用choose_time_init;
  • choose_time_init被定义为hpet_time_init,如果没有hpet,就去执行setup_pit_timer,
  • setup_pit_time注册一个事件pit_clockevent为Clockevent 设备并赋给global_clock_event(global_clock_event = &pit_clockevent),这个设备数据结构为irq0
  • 最后执行time_init_hook()来设置系统中断处理程序,调用setup_irq(0, &irq0)把irq0代表的action注册到链表上

Linux中的时钟和定时测量

动态定时器

数据结构

  • expires用于和系统核心变量jiffies进行比较
  • 成员变量function:定时器超时处理函数,该函数指针变量保存了内核定时器超时后要执行的函数;
  • 成员变量data:该无符号长整型变量是定时器超时处理函数的参数
  • 成员变量base:该指针变量表明了该内核定时器节点归属于系统哪一个处理器;

创建并**一个动态定时器

  • 创建一个新的timer_list对象
  • 调用init_timer初始化,并设置定时器要处理的函数和参数
  • 设置定时时间
  • 使用add_timer加入到合适的链表中

通常定时器只能执行一次,如果要周期性的执行,必须再次将其加入链表
Linux中的时钟和定时测量