- 论坛徽章:
- 0
|
关键是0号中断。
麻烦之处是: HPET和PIT的代码都要分析, 而且还要涉及到 教新的 hrtimer和 dynamic tick,以及相关的clock event 模型。 等。
这里有一份带着很多问题的笔记:
==============================
PIT的ISR是timer_interrupt, LAPIC Timer的ISR是smp_apic_timer_interrupt。
塑料袋这么说的: SMP上的调度,每个CPU(不管是BSP还是AP)都只用LAPIC Timer来做。
问: 为什么用LAPIC Timer?
答: kernel想把各个CPU的调度时间错开
问: 那么PIT中断用来做什么?
答: 只是更新jiffies。
问: PIT的ISR谁来执行? 一定是BSP吗?
答: 不一定,服从IO-APIC和LAPICs的仲裁。
问: 那怎么仲裁? IO-APIC的IRQ redirection table中,PIT占的那个entry,其destination
怎么设置?
答(by alb): 不知道IO-APIC怎么设置那个IRTE。但是0号中断只能有CPU0来处理,这是在irq0这
个irqaction的mask成员中规定好了的。(见下)
谨案:(from 2.6.25-rc7/rc8)
==========================
hrtimer引入到内核中(2.6.17?)时,增强了clock event模型。 在当前内核里,有3种中断源将自己注册为一个
clock_event_device:
1) LAPIC Timer (每个CPU一个lapic_events)
arch/x86/kernel/apic_32.c, setup_APIC_timer():
clockevents_register_device(levt);
嗯,lapic_clockevent是每个lapic_events的默认值,每个CPU都在setup_APIC_Timer中用它对lapic_events
进行初始化。
2) HPET
arch/x86/kernel/hpet.c, hpet_legacy_clockevent_register():
clockevents_register_device(&hpet_clockevent);
3) PIT
arch/x86/kernel/i8253.c, setup_pit_timer():
clockevents_register_device(&pit_clockevent);
同时,系统中有一个唯一的global_clock_event指针,从代码上来看,它或者被赋值为&pit_clockevent,或者
被赋值为hpet_clockevent。 我的理解是,只要CONFIG_HPET_TIMER=y,那么就是后者。 而且,LAPIC Timer只
能是一种Local Interrupt Source,因此不可能用来给global_clock_event赋值。
//FIXME
现在的关键问题是: 如何确定这3种clockevent的event_handler成员分别是在哪里赋值的? 各自赋值为谁?
注意这3个静态数据结构:hpet_clockevent、pit_clockevent和lapic_clockevent都没有初始化其event_handler
成员。
这个问题在后边回答。
内核中检查0号中断的中断源为HPET还是PIT的代码流程是:
start_kernel() > time_init() > choose_time_init() == hpet_time_init:
void __init hpet_time_init(void)
{
if (!hpet_enable())
setup_pit_timer();
time_init_hook();
}
注意,如果HPET失败,则使用PIT。 做完这个选择之后进行0号中断的相关设置。
-> HPET的情况:
hpet_enable() > hpet_legacy_clockevent_register() :
clockevents_register_device(&hpet_clockevent);
global_clock_event = &hpet_clockevent;
-> PIT的情况:
setup_pit_timer() :
clockevents_register_device(&pit_clockevent);
global_clock_event = &pit_clockevent;
-> 设置完中断源后:
time_init_hook():
/**
* static struct irqaction irq0 = {
* .handler = timer_interrupt,
* .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL,
* .mask = CPU_MASK_NONE,
* .name = "timer"
* };
*/
irq0.mask = cpumask_of_cpu(0);
setup_irq(0, &irq0);
可见, 不管是其中断源是HPET还是PIT, 0号中断只能由CPU0来处理,其处理例程为timer_interrupt。
event_handler的赋值:
===================
注意这3个静态数据结构:hpet_clockevent、pit_clockevent和lapic_clockevent都没有初始化其
event_handler。
注意注册一个clockevents设备时:
clockevents_register_device() > clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev) >
raw_notifier_call_chain(&clockevents_chain, reason, dev) >
__raw_notifier_call_chain(nh, val, v, -1, NULL) >
notifier_call_chain(&nh->head, val, v, nr_to_call, nr_calls) :
...
ret = nb->notifier_call(nb, val, v); /* 注意这个参数v,就是clockevents_register_device
* 的参数clock_event_device *dev
*
* 而参数nb,就是&clockevents_chain
*/
这样就在clockevents_chain这个struct raw_notifier_head中注册了自己。
然而,这个notifier_call函数指针是什么东西? 赋值为哪个函数? 且看start_kernel() > tick_init() :
clockevents_register_notifier(&tick_notifier);
而tick_notifier的定义:
static struct notifier_block tick_notifier = {
.notifier_call = tick_notify,
};
于是我们知道,tick_init()时已经为clockevents_chain的notifier_call指针赋值,这个值就是tick_notify
函数:
tick_notify():
switch(reason) {
case CLOCK_EVT_NOTIFY_ADD:
return tick_check_new_device(dev);
...
}
而当初我们注册pit_clockevent和hpet_clockevent、lapic_clockevent时,就是指定了CLOCK_EVT_NOTIFY_ADD
去调用clockevents_register_device的。 于是:
tick_check_new_device() > (to be continued)
资料:
Documentation/hrtimers/highres.txt和 http://www.linuxsymposium.org/2005/linuxsymposium_procv1.pdf中的一篇论文:
Hrtimers and Beyond: Transforming the Linux Time Subsystems |
|