linux应用程序使用时钟中断,Linux时钟中断(2.6.23)(三)

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

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

timer_interrupt()

|-->do_timer_interrupt_hook();

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

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

1、global_clock_event是什么?

2、global_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_handler=tick_handle_periodic。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值