回到start_kernel,612行time_init函数:
void __init time_init(void)
{
late_time_init = x86_late_time_init;
}
函数x86_late_time_init实际上是初始化tsc时钟源。在time_init中只是把该函数的地址赋给全局变量late_time_init,以后某个时刻肯定会调用它的,这里先提前详细分析一下他:
static __init void x86_late_time_init(void)
{
x86_init.timers.timer_init();
tsc_init();
}
x86_init.timers.timer_init实际是函数hpet_time_init,来自文件kernel/timer.c:
101 void __init hpet_time_init(void)
102 {
103 if (!hpet_enable())
104 setup_pit_timer();
105 setup_default_timer_irq();
106}
由于我们没有配置CONFIG_HPET_TIMER,所以103行hpet_enable返回0,调用104行的函数setup_pit_timer,位于arch/x86/kernel/i8253.c:
104void __init setup_pit_timer(void)
105{
106 /*
107 * Start pit with the boot cpu mask and make it global after the
108 * IO_APIC has been initialized.
109 */
110 pit_ce.cpumask = cpumask_of(smp_processor_id());
111 pit_ce.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, pit_ce.shift);
112 pit_ce.max_delta_ns = clockevent_delta2ns(0x7FFF, &pit_ce);
113 pit_ce.min_delta_ns = clockevent_delta2ns(0xF, &pit_ce);
114
115 clockevents_register_device(&pit_ce);
116 global_clock_event = &pit_ce;
117}
整个函数的115行之前都是在通过汇编指令初始化全局变量pit_ce,这个clock_event_device类型的变量在编译的时候已经被初始化成了:
static struct clock_event_device pit_ce = {
.name = "pit",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_mode = init_pit_timer,
.set_next_event = pit_next_event,
.shift = 32,
.irq = 0,
};
我们看到它的irq号为0,也就是对应32号中断描述符表项,即interrupt[0]处理函数。115行把它注册到通知链和clockevent_devices链表中:
void clockevents_register_device(struct clock_event_device *dev)
{
unsigned long flags;
……//一些调试代码
raw_spin_lock_irqsave(&clockevents_lock, flags);
list_add(&dev->list, &clockevent_devices);
clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);
clockevents_notify_released();
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
最后将全局global_clock_event设置成这个定时器,然后回到hpet_time_init中,105行,调用setup_default_timer_irq():
void __init setup_default_timer_irq(void)
{
setup_irq(0, &irq0);
}
static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER,
.name = "timer"
};
很简单,调用setup_irq把interrupt[0]对应的处理函数设置成timer_interrupt。至于随后如何处理的,请查看博客“定时器中断”
http://blog.csdn.net/yunsongice/archive/2010/03/07/5354137.aspx