Skip to content

Latest commit

 

History

History
86 lines (52 loc) · 7.48 KB

2014-04-28-time-system.md

File metadata and controls

86 lines (52 loc) · 7.48 KB
layout title category description tags
post
时钟和定时器电路
定时测量
时钟和定时器电路...
时钟 定时器电路 RTC TSC PIT ACPI

在80x86体系结构上,内核必须显式地与几种时钟和定时电路打交道,时钟电路同时用于跟踪当前时间和产生精确的时间度量。定时器电路由内核编程,所以它们以固定的、预先定义的频率发出中断。这样的周期性中断对于实现内核和用户程序使用的软定时器都是非常重要的,这里有几种IBM兼容PC上的时钟和硬件电路。

实时时钟 - RTC

所有的PC都包含一个叫实时时钟(Renl Time Clock RTC)的时钟,它是独立于CPU和所有其他芯片的。

即使当PC被切断电源,RTC依旧继续工作,因为它靠一个小电池或蓄电池供电。CMOS TAM和RTC被继承在一个芯片1上。

RTC能在IRQ8上发出周期性的中断,频率在2~8192Hz之间,也可以对RTC进行编程以使当RTC到达某个特定的值时激活IRQ8线,也就是作为一个闹钟来工作。

Linux只用RTC来获取时间和日期,不过通对*/dev/rtc*设备文件进行操作,也允许进程对RTC编程。内核通过0x7x和0x71 I/O端口访问RTC。系统管理员通过执行Unix系统时钟程序可以设置时钟。

时间戳计数器 - TSC

所有的80x86微处理器都包含一条CLK输入引线,它接收玩不振荡器的时钟信号。从Pentium开始,80x86微处理器就都包含一个计数器,它在每个时钟信号到来时加1。该计数器是利用64位的时间戳计数器(Time Stamp Counter TSC)寄存器来实现的,可以通过汇编语言指令rdtsc来读这个寄存器。当使用这个寄存器时,内核必须考虑到时钟信号的频率,例如时钟节拍的频率时1GHz,那么时间戳计数器每那庙增加一次。

与可编程间隔定时器传递来的时间测量相比,Linux利用这个寄存器可获得更精确的时间测量,为了做到这点,Linux在初始化系统的时候必须确定时钟信号的频率。因为编译内核时并不声明这个频率,所以同一内核映象可以运行在产生任何时钟频率的CPU上。

算出CPU实际频率的任务时在系统初始化期间完成的。*calibrate_tsc()*函数通过计算一个大约在5ms的时间间隔内所产生的时钟信号的个数算出CPU实际的频率。通过适当地设置可编程间隔定时器的一个通道来产生这个时间常量。

在x86平台初始化的代码可以看到,calibrate_tsc被初始化为native_calibrate_tsc

<arch/x86/kernel/x86_init.c>

{% highlight c++ %} struct x86_platform_ops x86_platform = { .calibrate_tsc = native_calibrate_tsc, .get_wallclock = mach_get_cmos_time, .set_wallclock = mach_set_rtc_mmss, }; {% endhighlight %}

其中native_calibrate_tsc代码在文件*<arch/x86/kernel/tsc.c>*中。

可编程间隔定时器 - PIT

除了实时时钟和时间戳计数器,IBM兼容PC还包含第三种类型的时间测量设备叫做可编程间隔定时器(Programmable Interval Timer PIT)。PIT的作用类似于微波炉的闹钟,让用户意识到烹饪调整的时间见过已经过去了。不同的是,这个设备不是通过震铃,而是发出一个特殊的中断,叫做时钟中断(timer interrupt)来通知内核又一个时间间隔过去了。

Linux给PC的第一个PIT进行编程,使它以大约1000Hz的频率向IRQ0发出时钟中断,即每1ms产生一次时钟中断。这个时间间隔叫做一个节拍(tick),它的长度以纳秒为单位放在tick_nsec变量中。

一般而言,短的节拍产生较高分辨率的定时器,当这种定时器执行同步I/O多路复用1时,有助于多媒体的平滑播放和较快的响应时间。不过,这是一种折中,段的节拍需要CPU在内核态花费更多的时间,也就是在用户态花费较少的时间。因而,用户程序运行得稍微慢一些。

时钟中断的频率取决于硬件体系结构,较慢的机器节拍大约为10ms,而较快的机器节拍为大约1ms。在Linux代码中,有几个宏产生决定时钟中断频率的常量:

  1. HZ产生美妙时钟中断的近似个数,也就是时钟中断的频率,在IBM PC下其值设置为1000。
  2. CLOCK_TICK_RATE产生的值为1193182,这个值是8254芯片的内部震荡器频率。
  3. LATCH产生CLOCK_TICK_RATE和HZ的比值再四舍五入后的整数值,这个值用来对PIT编程。

CPU本地定时器

在最近的80x86微处理的本地APIC中还提供了另一种定时测量设备,即CPU本地定时器。

CPU本地定时器是一种能够产生单步中断或周期性中断的设备,它类似于刚才描述的可编程间隔定时器,不过,还是有几种区别:

  1. APIC计数器是32位,而PIC的计数器是16位,因此,可以对本地定时器编程来产生很低频率的中断。
  2. 本地APIC定时器把中断值发送给自己的处理器,而PIT产生一个全局性的中断,系统中的任一CPU都可以对其进行处理。
  3. APIC定时器是基于总线时钟信号的,每隔1,2,4,8,16,32,64或128总线时钟信号到来时对该定时器进行递减可以实现对其编程的目的。相反,PIT有其自己的内部时钟振荡器,可以更灵活的编程。

高精度事件定时器

高精度事件定时器是由Intel和Microsoft联合开发的一种新型定时器芯片,尽管这种定时器在终端用户机器上还不普遍,但Linux 2.6已经能够支持它们。

HPET提供了许多可以被内核使用的硬件定时器,这种新定时器芯片主要包含8个32位或64位的独立计数器。每个计数器由它自己的时钟信号所驱动,该时钟信号的频率必须至少位10MHz,因此,计数器最少可以每100ns增长一次。任何计数器最多可以与32个定时器相关联,每个定时由一个比较器和一个匹配寄存器组成。比较器是一组用于检测计数器中的值与匹配寄存器中的值是否匹配的电路,如果找到一组匹配值就产生一个硬件中断,一些定时器可以被激活来产生周期性中断。

可以通过映射到内存空间的寄存器来对HPET芯片编程。BIOS在自举阶段建立起映射并向操作系统内核报告它的起始内存地址。HPET寄存器允许内核对计数器和匹配寄存器的值进行读和写,允许内核对单步中断进行编程,还允许内核再支持HPET的定时器上激活或禁止周期性中断。

ACPI电源管理定时器

ACPI电源管理定时器2是另一种时钟设备,包含再几乎所有基于ACPI的主板上。它的时钟信号拥有大约位3.58MHz的固定频率。该舍比实际上是一个简单的计数器,它再每个时钟节拍到来时增加一次。为了读取计数器的当前值,内核需要访问某个I/O端口,该端口的地址由BIOS在初始化阶段确定。

如果操作系统或者BIOS可以通过动态降低CPU的工作频率或者工作典雅来节省电池的电能,那么ACPI电源管理定时器就比TSC更优越。当发生这种情况时,TSC的频率发生改变,而ACPI PMT的频率不会改变。另一方面,TSC计数器的高频率非常便于测量特别小的时间间隔。

不过,如果系统中存在HPET设备,那么比起其他的电路而言它总是首选,因为它更复杂的结构使得功能更强。

Footnotes

  1. Motorola 146818或其他芯片上。 2

  2. 也称ACPI PMT。