1. 时钟分类

现代计算机中, 操作系统中很多事件都是时钟驱动的, 例如进程调度、定时器等.

时钟根据工作方式不同, 可以分为如下两类.

1.1. 周期性时钟

周期性时钟(Periodic Timer): 时钟以固定频率产生时钟中断.

通常, 周期性时钟会有一个计数器,

  • 要么以固定值递减到0产生中断, 例如PIT;
  • 要么固定增长, 当达到某个阙值时产生中断, 同时自动将阙值增加一个固定值, 计数器继续递增, 例如HPET.

1.2. 单次计时时钟

单次计时时钟(One-shot Timer): 多数时钟都可以配置成这种方式, 例如PIT、HPET.

其工作方式和到达阙值产生中断的周期性时钟类似, 不同的是产生中断后阙值不会自动增加, 而是需要软件(通常是时钟中断处理函数)增加该阙值.

这提供给软件动态调整下一次时钟中断到来时间的能力, 使一些新技术,例如无滴答声内核(Tickles Kernel)的实现成为可能.

2. x86平台的常用时钟

由于历史原因, x86平台有多种时钟, 目前仍广泛使用的有以下几种.

2.1. PIT: 可编程间隔时钟

(1) PIT: Programmable Interrupt Timer或Programmable Interval Timer, 可编程中断/间隔时钟

IBM PC平台产生, 频率为1000Hz左右, 即每次中断间隔约为1ms, 通常接IRQ0产生周期性的时钟中断信号!!! 来充当系统定时器, 软件可以通过0x40~0x43 I/O端口进行操作.

通过古老的8259pic 0号中断响应 timer_interrupt 这个中断处理函数

PIT是一种低精度的时钟, 容易溢出(16位), 已渐渐被高精度时钟取代. 现在的Linux基本上不怎么用它. 说白了它就是个硬件实现的计数器系统,通过时钟中断来满足操作系统的定时器需求,通过写某些位来设置定时器,操作系统同时也要维护一套软件的定时器链表,与这个硬件链表进行对应

PIT支持周期性单次计时两种方式.

8254 PIT有3个计时通道, 每个通道都有其不同的用途:

  • 通道0用来负责更新系统时钟. 它在每个时钟滴答会通过IRQ0向系统发出一次时钟中断信号.
  • 通道1通常用来控制DMAC对RAM的刷新
  • 通道3被连接到PC机的扬声器, 以产生方波信号.

2.2. RTC: 实时时钟

(2) RTC(Real Time Clock, 实时时钟): 通常和CMOS集成在一起, 由CMOS电池供电, 故能在关机后继续计时.

开机时读取,需要的时候写入,它能够提供稳定的时钟脉冲,其频率范围是2~8192Hz, 通常接IRQ8, 软件可以通过0x70~0x71 I/O端口操作.

RTC支持周期性单次计时两种方式.

此外, 它还可以配置成每秒产生一次中断!!!, 具有闹钟功能. 由于具有关机继续计时的功能, RTC常被用来作为操作系统提供日期. 即"年/月/日".

在Linux中,可以用RTC来获取当前的时间。它提供的稳定的脉冲就是可编程计数器的最底层。也就是说它是PIT和HPET的源头。

2.3. TSC: 时间戳计数器

(3) TSC(Time Stamp Counter, 时间戳计数器): 和普通时钟不同, 可以看作一个单调递增的计数器(64位), 是由x86架构引入的.

TSC在每个时钟信号到来时加1.

TSC的主体是位于CPU里面的一个64位的TSC寄存器。每个CPU时钟周期其值加一。

时钟频率CPU的频率相关, 操作系统在使用前需要计算其频率, 例如1GHz的TSC, 其值每纳秒增加1.

它是CPU内部的一个寄存器,根据CPU的周期进行递增. 通过rdtsc指令, 可以读取当前TSC的值. 通过它也可以校准RTC。很多系统都是通过读取这个时间来作为系统的时间

由于不产生时钟中断!!!, 故无所谓周期性和单次计时方式.

2.4. LAPIC Timer: 本地时钟中断

(4) LAPIC Timer: 该时钟是根据LAPIC所在总线(系统总线APIC总线)频率产生的.

这个是针对smp系统的时钟,当系统没有本地时钟的时候,也可以通过时钟广播的方式模拟这个本地时钟,在smp系统中linux会优先选择这个时钟源,一旦使用了这个时钟,0号中断绑定的时钟源的profiling和update功能将会被switched off。比如pit将会从periodic转变为oneshot。所以只要打开了local apic,将优先使用这个时钟源。它的中断处理函数smp_apic_timer_interrupt。它在x86 arch里面的entry_64.s汇编文件中built。有点复杂。实现在apic.c中。

为32位, 有如下两个特点.

⓵ 由于LAPIC是每个CPU一个, 故其中断也是对于本地CPU的(Per CPU Interrupt).

⓶ 可通过寄存器配置, 对总线周期进行不同的分频而产生不同频率的时钟中断!!!.

LAPIC Timer可配置成周期性单次计时两种工作方式.

2.5. HPET: 高精度时钟

(5) HPET(High Precision Event Timer, 高精度事件时钟): Intel和微软共同开发的新型高精度时钟, 最低频率10MHz, 可作为64位或32位时钟使用.

HPET可以提供最多8个时钟, 典型实现至少有一个时钟可用.

HPET时钟通过一个主计数器, 和32个比较器、匹配器一起, 又可以被配置成32个子时钟(又称channel), 每个子时钟可按不同频率产生不同的中断.

例如, 可将一个子时钟配置成每毫秒产生一个IRQ8中断, 另一个子时钟可配置成每毫秒产生一个IRQ0中断.

HPET可用于代替传统PIT和RTC, 此时平台的IRQ0IRQ8中断被HPET占用。 cpu内部时钟源,同样响应0号中断响应timer_interrupt。Linux中hpet已经取代了pit,有hpet系统先选择hpet.

HPET支持周期性单次计时两种工作方式。

同时使用多个时钟带来一个明显缺点是过多时钟中断会影响系统的性能(!!!).

所以, 当高精度时钟可用, OS通常会禁用低精度时钟(!!!), 并根据需要使用高精度时钟模拟低精度时钟.

3. 操作系统的时钟观

两个系统时钟的常识jiffies和xtimer,前者是linux系统时钟的累计数字,后者是系统的墙上时间。

从操作系统角度看, 时钟作用可以分为如下两类.

3.1. 统计值和驱动事件

⓵ 提供统计值驱动事件:

  • 提供统计值是指操作系统用时钟来维护一些必需的数据, 例如一个进程用户态/内核态的时间, 系统的日期、时间等.
  • 驱动事件是指驱动以时间为资源的程序!!!, 典型就是进程. 例如, 分时操作系统为每个进程分配固定的时间片, 调度时间片耗尽的进程睡眠, 唤醒分配到新时间片的进程运行.

3.2. 定时器

维护定时器(Timer): 定时器是程序中常用的组件, 用于在某个指定时间到达后执行特定的操作. 定时器大量运用于操作系统中, 例如内核为I/O操作注册的超时定时器、操作系统提供给应用程序使用的定时器接口等. 见图2-19.

7. 时钟

图中可看到, 操作系统使用时钟的功能, 是以时钟中断为基础的. 要了解时钟的作用, 可以以时钟中断为脉络, 勾画出整个框图.

操作系统往往会对时钟架构进行封装以方便维护和使用. 但从硬件角度看, 时钟中断仍然是所有封装的基础, 故虚拟化对时钟的处理主要是提供准确的时钟中断模拟硬件的行为.

相关文章: