分类: LINUX
2008-09-17 18:08:22
在讲时钟中断以前,我们先来了解一些与时钟有关的硬件处理:
1):实时时钟(RTC)
该时钟独立于CPU和其它芯片.即使PC断电,该时钟还是继续运行.该计时由一块单独的芯片处理,并把时钟值存放CMOS.该时间可参在IRQ8上周期性的产生时间信号.频率在2Hz
~
8192Hz之间.但在linux中,只是用RTC来获取当前时间.
2):时间戳计时器(TSC)
CPU附带了一个64位的时间戳寄存器,当时钟信号到来的时候.该寄存器内容自动加1
3):可编程中断定时器(PIC)
该设备可以周期性的发送一个时间中断信号.发送中断信号的间隔可以对其进行编程控制.在linux系统中,该中断时间间隔由HZ表示.这个时间间隔也被称为一个节拍(tick).
4):CPU本地定时器
在处理器的本地APIC还提供了另外的一定定时设备.CPU本地定时器也可以单次或者周期性的产生中断信号.与上次描述的PIC相比.它有以下几点的区别:
APIC本地计时器是32位.而PIC是16位.由此APIC本地计时器可以提供更低频率的中断信号
本地APIC只把中断信号发送给本地CPU.而PIC发送的中断信号任何CPU都可以处理
APIC定时器是基于总线时钟信号的.而PIC有自己的内部时钟振荡器
5):高精度计时器(HPET)
在linux2.6中增加了对HPET的支持.HPET是一种由微软和intel联合开发的新型定时芯片.该设备有一组寄时器,每个寄时器对应有自己的时钟信号,时钟信号到来的时候就会自动加1.
实际上,在intel多理器系统与单处理器系统还有所不同:
在单处理系统中.所有计时活动过由PIC产生的时钟中断信号触发的
在多处理系统中,所有普通活动是由PIC产生的中断触发.所有具体的CPU活动,都由本地APIC触发的.
HPET
Support
HPET Timer Support 是一个新的特性,HPET
是intel
制定的新的用以代替传统的8254(PIT)中断定时器与RTC
的定时器,全称叫作高精度事件定时器。如果你有一台较新的机器就选它吧,一般它是一个安全的选项,即使你的硬件不支持HPET
也不会造成损害.
以上内容摘自http://hi.baidu.com/80695073/blog/item/9c170f55be0ae5c1b745ae5b.html
在2.6.23内核中,我们知道当一个中断发生时,中断的执行过程是内核经过一系列的跳转最后调用do_IRQ()来相应(处理)该中断。而do_IRQ()最终调用的是注册在irq_desc数组对应项的处理函数。例如对于时钟中断来说,最终处理时钟中断的函数是注册在irq_desc[0]中的处理函数。接下来我们就看看到底注册的时钟中断处理函数是什么。
我们使用命令cat /proc/interrupt可以查看到当前系统中已经注册的中断,其中时钟中断的中断号为0,由此我们可以知道系统的时钟中断处理函数是注册在irq_desc[0](更多关于irq_desc数组的信息请查看include/linux/irq.h)里的中断处理函数。由/proc/interrupt我们也可以知道irq_desc[0].name=”timer”。由此我们可以在内核里找到irq0:
static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
.mask = CPU_MASK_NONE,
.name = "timer"
};
该结构给出了两个信息。一个是0号中断的名字是“timer”,另一个是中断的处理函数是timer_interrupt。由此我们可以断定irq0便是时钟中断描述符。它的注册过程必然是setup_irq(0,&irq0)。这一节我们主要讲时钟中断的注册过程,下一节讲时钟中断处理函数timer_interrupt。
我们使用cscope(一个很好用的在本地阅读内核源码的工具)可以很轻松的找到一个函数在内核里都被那些函数调用。所以我们找到了timer_init_hook(),这里调用setup_irq(0,&irq0)来注册时钟中断。依次我们找到了时钟中断的注册过程:
start_kernel()
|-->time_init()
|-->tsc_init();
|-->late_time_init=choose_time_init();/*choose_time_init()=hpet_time_init*/
|-->late_time_init()
|-->time_init_hook(); /*在hpet_enable()函数调用之后*/
|-->setup_irq(0, &irq0);
|-->irq_desc[0].action.handler=timer_interrput;
我们都知道linux内核启动的C程序入口就是start_kernel,在start_kernel里,内核会进行一系列的初始化,其中就包括时钟中断。start_kernel()调用time_init()来初始化时间戳计数器(Time Stamp Counter TSC),另外将函数指针late_time_init赋值为hpet_time_init。之后在start_kernel()里有:
if (late_time_init)
late_time_init();
先前在time_init()里已经将late_time_init赋值为hpet_time_init了,所以此处便是调用hpet_time_init()函数。该函数就会调用time_init_hook()。这样时钟中断就被注册了。