- 很多计算机化的活动都是由定时测量来驱动的, 程序需要能每个文件中检索到文件的最后访问时间 (时间戳)
- Linux 内核需要完成的两种主要的定时测量:
- 保存当前时间和日期
- 维持定时器
- 定时测量是由基于固定频率振荡器和计数器的几个硬件电路完成的。
1. 时钟和定时器电路(不同的时钟触发源)
- 定时器电路由内核编程, 所以他们以固定的, 预先定义的频率发出中断。
1.1 实时时钟RTC
- 即使电源被切断, RTC 还会继续工作, 因为他有独立的电源供电
- Linux 只用RTC来获取时间和日期信息
1.2 时间戳计数器TSC
- 所有8086微处理器都有个 CLK 输入引线, TSC 在每个时钟信号到来的时候加1
- 可以使用 rdtsc 读取TSC 寄存器的值
- 与可编程间隔定时器传递的时间测量相比, TSC 可以获取更精确的时间测量
1.3 可编程间隔定时器PIT
- PIT 永远以内核固定的频率不停的发出中断 (时钟中断)
1.4 CPU 本地定时器
- CPU 本地定时器是一种可以产生单步中断或者周期性中断的设备, 类似于PIT
- 他可以产生比PIT 更低的频率
- APIC定时器只会把中断发送给自己的处理器, 而PIT 产生的是全局中断(系统中的任意CPU 都可以处理)
- APIC 定时器是基于总线时钟信号的, 而PIT 有自己的内部时钟振荡器(可以灵活编程)
1.5 高精度事件定时器 HPET
高精度事件定时器
1.6 ACPI 电源管理定时器
- 如果操作系统或者BIOS 可以通过动态降低CPU 的工作频率或者工作电压来节省电池电能, 那么ACPI定时器就比TSC 优越(因为, 此时 TSC 的频率发生了变化)
- 但是, TSC 计数器的高频率特性, 非常便于测量非常小的时间间隔
2. Linux 计时体系结构
- 单处理器系统, 所有计时活动活动都是全局定时器(可以是可编程间隔定时器, 也可以是高精度事件定时器)
- 多处理器系统, 所有活动都是由全局定时器产生的中断触发, 而具体的CPU 活动是由本地APIC 定时器产生的中断触发的
2.1 计时体系机构的数据结构
2.1.1 定时器对象
- 这是一个timer_opts 类型的描述符
2.1.2 jiffies变量
- jiffies 变量是一个计数器, 用来记录自系统启动以来产生的节拍总数。
- jiffies 在系统启动的时候被初始化为 0xfffb6c20, 这是一个32bit 的有符号值, 正好等于 -300000. 计数器会在系统启动后的5分钟内处于溢出状态(用来排除有缺陷的内核代码)
- 在 8086 体系中, jiffies变量通过连接器被换算成一个64 bit计数器的低 32 bit, 这个计数器称为 jiffies_64 (8086 是32bit, 无法直接读取 64 bit 值)
- 使用顺序锁读取jiffies_64 的值
2.1.3 xtime变量
- 存放当前时间和日期, 这是一个 timespec 类型的数据结构
- xtime_lock顺序锁消除了对 xtime 变量的同时访问而可能产生的竞争条件 ( 当然, 他也同时保护了 jiffies_64 变量)
2.2 单处理器系统上的计时体系结构
- 单处理器系统上, 所有与定时有关的活动都是由IRQ 线0 上可编程间隔定时器产生的中断触发的
2.3 多处理器系统上的计时体系结构
- 多处理器系统可以依赖两种不同的时钟中断源: 可编程间隔定时器或者高精度事件定时器产生的中断, 以及CPU 本地定时器产生的中断。
3. 更新时间和日期
- 用户程序从xtime 变量中获得当前时间和日期。 内核必须周期性的更新该变量, 才能使他的值保持相当的精确。(update_times)
4. 更新系统统计数
- 更新本地CPU统计数
- 记录系统负载
- 监管内核代码 (蒙特卡洛算法)
- 检查非屏蔽中断NMI 监视器
5. 软定时器和延迟函数
- linux 主要考虑两种类型的定时器: 动态定时器(内核), 间隔定时器(用户态进程)
- 由于对定时器函数的检查总是由可延迟函数进行, 而可延迟函数被激活以后很长时间才能被执行, 因此, 内核不能保证定时器函数在定时到期时开始执行, 而只能保证在适当的时候时间来执行他们。 (动态)
6. 与定时测量相关的系统调用
- time 和 gettimeofday
- adjtimex
- settimer 和 alarm
- 其他