您的位置:首页 > 其它

ARM中断源之定时器中断

2018-02-02 14:14 155 查看

实时时钟请求中断。在控制中遇到定时检测和控制,为此常采用一个外部时钟电路(可编程)控制其时间间隔。需要定时时,CPU发出命令使时钟电路开始工作,一旦到达规定时间,时钟电路发出中断请求,由CPU转去完成检测和控制工作。

走到这里,大家肯定对Linux的中断处理有概念了,下面我们通过一个具体的实例,来了解Linux内核处理中断的全过程,那就是定时器中断。在详细分析这个过程之前,我们把Linux时间管理的概念先缕一缕。

 

在当前的80x86体系结构上,内核显式地与几种时钟和定时器电路打交道,其主要分为了时钟和定时器两大类:

- 时钟电路同时用于跟踪当前时间和产生精确的时间度量。

- 定时器电路由内核编程,所以它们以固定的、预先定义的频率发出中断。

 

1、实时时钟(RTC)

 

所有的PC都包含一个叫实时时钟(Renl Time Clock RTC)的时钟,它是独立于CPU和所有其他芯片的。即使当PC被切断电源,RTC还继续工作,因为它靠一个小电池或蓄电池供电。CMOS RAM和RTC被集成在一个芯片上。

 

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

 

Linux只用RTC来获取时间和日期,不过,通过对/dev/rtc设备文件进行操作,也允许透程对RTC编程。内核通过0x70和Ox71 I/O端口访问RTC。系统管理员通过执行Unix系统时钟程序(直接作用于这两个I/O端口)可以设置时钟。MC146818 RTC芯片(或其他兼容芯片,如DS12887)可以在IRQ8上产生周期性的中断,中断的频率在2HZ~8192HZ之间。与MC146818 RTC对应的设备驱动程序实现在include/linux/rtc.h和drivers/char/rtc.c文件中,对应的设备文件是/dev/rtc(major=10,minor=135,只读字符设备)。因此用户进程可以通过对她进行编程以使得当RTC到达某个特定的时间值时激活IRQ8线,从而将RTC当作一个闹钟来用。

而Linux内核对RTC的唯一用途就是把RTC用作“离线”或“后台”的时间与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期的基准值。然后再运行期间内核就完全抛开RTC,从而以软件的形式维护系统的当前时间与日期,并在需要时将时间回写到RTC芯片中。所以,RTC时钟只是个为后面我们介绍的那些时钟起一个初始化的作用,仅此而已!

Linux在include/linux/mc146818rtc.h和include/asm-i386/mc146818rtc.h头文件中分别定义了mc146818 RTC芯片各寄存器的含义以及RTC芯片在i386平台上的I/O端口操作。而通用的RTC接口则声明在include/linux/rtc.h头文件中。

 

2、时间戳计数器(TSC)

 

时间戳计数器与实时时钟配合使用,用来将时钟和定时器调整得更精确。所有的80x86微处理器都包含一条CLK输入引线,它接收外部振荡器的时钟信号。从Pentium开始,80x86微处理器就都包含一个计数器,它在每次外部振荡器的时钟信号到来时加1。该计数器是利用64位的时间戳计数器(Time Stamp Counter TSC)寄存器来实现的,可以通过汇编语言指令rdtsc读这个寄存器。

 

为什么要出现一个时间戳计数器呢?Linux利用这个寄存器可获得更精确的时间量,以便计算进程用户态与内核态的运行时间及等待(睡眠)时间。为了做到这点,Linux在初始化系统的时候必须确定时钟信号的频率。事实上,因为编译内核时并不声明这个频率,所以同一内核映像可以运行在产生任何时钟频率的CPU上,即这个频率由CPU决定。

 

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

 

3、可编程间隔定时器(PIT),重点!

 

可编程间隔定时器(Programmable Interval Timer PIT)的作用类似于微波炉的闹钟,即让用户意识到烹调的时间间隔已经过了。所不同的是,这个设备不是通过振铃,而是发出一个特殊的中断,叫做时钟中断(timer interrupt)来通知内核又一个时间间隔过去了。每个IBM兼容PC都至少包含一个PIT,PIT通常是使用0x40~0x43 I/O端口的一个8254 CMOS芯片,该芯片作为I/O设备与中断控制器的IRQ0相连,向量号为32。

 

Linux给PC的PIT进行编程,使它以1000 Hz的频率向IRQ0发出时钟中断,即每1ms产生一次时钟中断。这个时间间隔叫做一个节拍(tick),它的长度以纳秒为单位存放在tick_nsec变量中。在PC上,tick_nsec被初始化为999848ns(产生的时钟信号频率大约为1000.15 Hz),但是如果计算机被外部时钟同步的话,它的值可能被内核自动调整。

 

时钟中断的频率可以通过编译内核前对一些参数的设定来满足于具体硬件体系结构的要求。较慢的机器,其节拍大约为lOms(每秒产生100次时钟中断),而较快的机器的节拍为大约1ms(每秒产生1000或1024次时钟中断)。

 

在Linux的代码中,有几个宏产生决定时钟中断频率的常量,对此讨论如下:

- HZ产生每秒时钟中断的近似个数,也就是时钟中断的频率。在IBM PC上,这个值设置为1000。

- CLOCK_TICK_RATE产生的值为1193182,这个值是8254芯片的内部振荡器频率。

- LATCH产生CLOCK_TICK_RATE和HZ的比值再四舍五入后的整数值。这个值用来对PIT编程。

 

PIT由setup_pit_timer()进行如下初始化:

    spin_lock_irqsave(&i8253_lock, flags);

    outb_p(0x34,0x43);

    udelay(10);

    outb_p(LATCH & 0xff, 0x40);

    udelay(10);

    outb(LATCH >> 8, 0x40);

    spin_unlock_irqrestore(&i8253_lock, flags);

 

outb()C函数等价于outb汇编语言指令:它把第一个操作数拷贝到由第二个操作数指定的I/O端口。outb_p()函数类似于outb(),不过,它会通过一个空操作而产生一个暂停,以避免硬件难以分辨。udelay()宏函数引入了一个更短的延迟。

 

第一条outb_p()语句让PIT以新的频率产生中断。接下来的两条outb_p()和outb()语句为设备提供新的中断频率。把16位LATCH常量作为两个连续的字节发送到设备的8位I/O端口0x40。结果,PIT将以(大约)1000Hz的频率产生时钟中断,也就是说,每1ms产生一次时钟中断。

 

4、其他定时器

 

除了PIT,80x86体系还有其他几个定时器,这里只简单提一提:

- CPU本地定时器:CPU本地定时器是一种能够产生单步中断或周期性中断的设备,向量范围是239 (0xef),比PIT更灵活地编程。

- 高精度事件定时器(HPET):可以通过映射到内存空间的寄存器来对HPET芯片编程的定时器。

- ACPI电源管理定时器:时钟信号拥有大约为3.58 MHz的固定频率,专门针对ACPI电源的定时器。

 

本博文,我们重点讨论第三项,即可编程间隔定时器,它是整个Linux内核的心脏,驱动着若干进程的运行。PS,这里只讨论单CPU的计时体系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: