Chinaunix首页 | 论坛 | 博客
  • 博客访问: 952272
  • 博文数量: 113
  • 博客积分: 7235
  • 博客等级: 少将
  • 技术积分: 2101
  • 用 户 组: 普通用户
  • 注册时间: 2008-07-14 11:24
文章分类

全部博文(113)

文章存档

2013年(7)

2012年(5)

2011年(6)

2010年(8)

2009年(15)

2008年(72)

分类: LINUX

2008-09-20 22:02:24

上一节主要讲了时钟中断的注册过程,在这一节我们主要时钟中断的处理程序。

由上一节我们知道注册的时钟中断处理程序是timer_interrupt。我们使用cscope在内核里可以搜索到如下调用关系:

timer_interrupt()

|-->do_timer_interrupt_hook();

   |-->global_clock_event->event_handler(global_clock_event);

到此我们又遇到了新的问题:

1global_clock_event是什么?

2global_clock_event->event_handler指针最终指向的那个函数?

首先global_clock_event是一个struct clock_event_device类型的全局指针变量,保存着系统中当前正在使用的时钟事件设备(保存了系统当前使用的硬件时钟中断发生时,要执行的中断处理函数的指针)。接下来我们使用cscope工具搜索内核就可以知道在被赋值为:

global_clock_event = &pit_clockevent;或者global_clock_event = &hpet_clockevent;现在的硬件大部分都支持HPET,所以在这里我们主要看hpet_clockevent

static struct clock_event_device hpet_clockevent = {

.name = "hpet",

.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,

.set_mode = hpet_set_mode,

.set_next_event = hpet_next_event,

.shift = 32,

.irq = 0,

};

可以看到在内核里时钟时间设备为hpet_clockevent。到此我们依然没有找到。前面提到的将global_clock_event赋值为hpet_clockevent是在hpet_enable()函数中。上一节我们提到时钟中断的注册中调用了late_time_init()函数(也即hpet_time_init函数),在这个函数中就会首先调用hpet_enable()函数,追踪之后函数的调用流程如下:

start_kernel()

|-->time_init()

       |-->tsc_init();late_time_init=choose_time_init();/*choose_time_init()=hpet_time_init*/

             |-->late_time_init()

          |-->hpet_enable()/*如果硬件不支持hpet,则调用setup_pit_timer*/

             |-->clockevents_register_device(&hpet_clockevent);

                |-->clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, &hpet_clockevent);

                    |-->raw_notifier_call_chain(&clockevents_chain,

                                                                       CLOCK_EVT_NOTIFY_ADD, &hpet_clockevent);

                                            |--> __raw_notifier_call_chain(nh, val,&hpet_clockevent, -1,

                                                                                                                                  NULL);

                                                  |-->notifier_call_chain(&nh->head, CLOCK_EVT_NOTIFY_ADD,

                                                                                     &hpet_clockevent, nr_to_call, nr_calls);

                                                        |-->nb->notifier_call(nb, CLOCK_EVT_NOTIFY_ADD,

                                                                                                                   &hpet_clockevent);

到此我们又遇到了一个问题,那就是clockevents_chain。使用cscope工具可以找到如下信息:

static struct notifier_block tick_notifier = {

   .notifier_call = tick_notify,

};


start_kernel() /*early than time_init*/

|-->tick_init()

   |-->clockevents_register_notifier(&tick_notifier);

             |-->raw_notifier_chain_register(&clockevents_chain, nb);/*nb=&tick_notifier*/

                   |-->notifier_chain_register(&nh->head, n);/*nh=&clockevents_chain,n=nb*/

                         |-->rcu_assign_pointer(*nl, n);


#define rcu_assign_pointer(p, v) ({ \

    smp_wmb(); \

    (p) = (v); \

})

由此可见clockevents_chain->head->notifier_call最终被注册为tick_notify.结合前面提到的对hept_clockevent的操作的最后一个函数nb->notifier_call(nb, CLOCK_EVT_NOTIFY_ADD, &hpet_clockevent);,我们就可以将该句写为:

tick_notify(clockevents_chain->head, CLOCK_EVT_NOTIFY_ADD, &hpet_clockevent)

下面我们来看看tick_notify函数:

tick_notify(clockevents_chain->head,CLOCK_EVT_NOTIFY_ADD,&hpet_clockevent)

|-->tick_check_new_device(&hpet_clockevent);

   |-->tick_setup_device(td, newdev, cpu, cpumask);/*newdev=&hpet_clockevent*/

           {

                      if (td->mode == TICKDEV_MODE_PERIODIC)

                                tick_setup_periodic(newdev, 0);

                     else

                                tick_setup_oneshot(newdev, handler, next_event);

           }


enum tick_device_mode {

  TICKDEV_MODE_PERIODIC,

  TICKDEV_MODE_ONESHOT,

};

我们这里只看tick_setup_periodic(&hpet_clockevent,0)这种情况,也就是时钟中断的类型为

TICKDEV_MODE_PERIODIC 这种情况。

tick_setup_periodic(&clockevents_chain,0)

|-->tick_set_periodic_handler(dev,0);

    |-->void tick_set_periodic_handler(struct clock_event_device *dev, int broadcast)

       {

            if (!broadcast)

                dev->event_handler = tick_handle_periodic;

            else

                 dev->event_handler = tick_handle_periodic_broadcast;

        }

到此我们终于知道了hpet_clockevent->event_handler=tick_handle_periodic。结合前面讲的所有,也就是说 在时钟中断处理函数timer_interrupt()函数里调用的global_clock_event->event_handlertick_handle_periodic

到此我们就彻底搞清了中断的注册和调用过程,不过也都是之从代码的角度去讲解的,却不知道为何要使用这么复杂的过程?目的何在?下一次我们具体研究在没一次时钟中断到来的时候,系统都做了那些工作,也就是tick_handle_periodic()函数。

阅读(3829) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

wenzhuW2008-12-25 15:21:19

请问一下: hpet有periodic和one shot两种模式,那么内核初始化的时候是在哪里决定选择使用哪种模式呢?