IRQ0中断处理全过程
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
比较重要的调用就是init_ISA_irqs();
③init_ISA_irqs()在同一个文件里
void init_ISA_irqs (void)
{ int i;
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
}
老的系统定时器如下组织:
timer_table[32] timer_struct
Timer_struct
Timer_active
31 0
⑤run_timer_list()也差不多是这样组织的。
Timer_head timer_list timer_list
Struct irq_action
3当一个时钟中断(IRQ0)到来,系统是如何处理的
①触发IRQ0时,CPU跳到IRQ0X00_interrupt的程序。
类式汇编代码如下:
IRQ0X00_interrupt:
Pushl 0x00-256
Jmp common_interrupt
②common_interrup在\arch\i386\kernel\irq.h一定如下:
irq_desc[irq].handler->handle(irq, ®s);
if (1) {
if (bh_active & bh_mask)
do_bottom_half();/*处理下半部分*/
}
}
其实这里irq_desc[irq].handler->handle(irq, ®s);就是do_8259A_IRQ(irq,®
};
do_8259A_IRQ()是个重要的函数,是个IRQ响应处理程序。
同一个文件下,去掉那些不相关的部分,如下:
static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
{ struct irqaction * action;
#define BUILD_COMMON_IRQ() \
__asm__( \
"\n" __ALIGN_STR"\n" \
"common_interrupt:\n\t" \
SAVE_ALL \
"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
"jmp ret_from_intr\n");
mark_bh(TIMER_BH);/*标记timer的下半部*/
if (!user_mode(regs))
lost_ticks_system++;/*如果在系统模式下
*在系统模式下丢失的滴答数加1
*/
if (tq_timer)/*判断定时器队列中有无任务*/
mark_bh(TQUEUE_BH);/*如果有,标记定时器队列的下半部分*/
rrupt().
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer",
NULL, NULL};
现在关于IRQ0的初始化完成了。变成如下图示:
irq_desc_t irq_desc[]
struct hw_interrupt_type
i8259A_irq_type定义如下:
static struct hw_interrupt_type i8259A_irq_type = {
"XT-PIC",
startup_8259A_irq,
shutdown_8259A_irq,
do_8259A_IRQ,
enable_8259A_irq,
disable_8259A_irq
⑤在do_timer_interrupt中又调用do_timer(\kernel\sched.c)。其他东西现在不感兴趣
。
⑥void do_timer(struct pt_regs * regs)
{
/*全局变量jiffies加1*/
(*(unsigned long *)&jiffies)++;
lost_ticks++;/*丢失的定时器滴答的数目*/
s),
对于IRQ0来说,它会间接的调用timer_interrupt(),上面已经讲过。
④timer_interrupt(\arch\i386\kernel\Time.c)是内核接收到IRQ0之后使用的函数,从
CPU时间截记数器中读取一些属性值。如果有值,就调用do_timer_interrup
t(同一个文件中)。
③do_IRQ在\arch\i386\kernel\irq.c
void do_IRQ(struct pt_regs regs)
{
int irq = regs.orig_eax & 0xff; /* subtle, see irq.h */
int cpu = smp_processor_id();
kstat.irqs[cpu][irq]++;/*更新内核的一些统计数字*/
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
irq_exit(cpu, irq);
return status;
}
只注意这里一个函数:
action->handler(irq, action->dev_id, regs);
unsigned long bh_mask = 0;
void (*bΒιβλιοθήκη Baidu_base[32])(void);
bh_base[]
31 bh_active 0
Bottom half handler(timer_bh)
31 bh_mask 0
不好意思,画的这么难看:P
如果bh_mask的第N位被置为1,则表明bh_base[]中的第N个指针指向了一个Bottom half
有几个跟时钟中断(IRQ0)相关的函数。
init_IRQ();
sched_init();
time_init();
sched_init()就是注册时钟中断的下半部分,其实IRQ0两个下半部分,一个前面已经看
到,还有一个下半部分init_bh(TQUEUE_BH, tqueue_bh);下面会看到。
②init_IRQ函数在\arch\i386\kernel\irq.c
do {
if (active & 1)
(*bh)();
bh++;
active >>= 1;
} while (active);
}
③对于IRQ0来说,(*bh)();就是timer_bh(\kernel\Sched.c);
void timer_bh(void)
{/*这是定时器真正的下半部分*/
update_times();/*更新统计数字*/
__sti();/*如果没有设置SA_INTERRUPT标记,就允许中断*/
else
__cli();
status |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);/*多个action只有在这个IRQ共享的情况下*/
对于IRQ0,其实就是timer_interrupt函数。
不,其实现在还不是,等time_init()完就基本差不多了。
⑤time_init()在\arch\i386\kernel\Time.c中
感兴趣的就是setup_x86_irq(0, &irq0);作用就是给IRQ0增加一个操作。即timer_inte
irq_desc_t *desc = irq_desc + irq;
{if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status |= IRQ_INPROGRESS;
}
desc->status = status;
{
bh_base[nr] = routine;
atomic_set(&bh_mask_count[nr], 0);
bh_mask |= 1 << nr;
}
就是简单的设置bh_base和bh_mask.
看看这些的定义:
atomic_t bh_mask_count[32];
unsigned long bh_active = 0;
⑥时钟中断的另外一个下半底tqueue_bh(\kernel\Sched.c)
}
handle_IRQ_event(irq, regs, action);
}
④handle_IRQ_event()也在同一个文件里。
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqacti
on * action)
②void run_bottom_halves(void)
{
unsigned long active;
void (**bh)(void);
active = get_active_bhs();/*取得激活的下半底*/
clear_active_bhs(active);/*清那些激活的下半底*/
bh = bh_base;
例程。
如果bh_active的第N位被置为1,则表明一旦调度进程许可,立即调用第N个Bottom hal
f例程。
bh_mask_count[]跟踪为每个下半部分提出的enable/disable请求嵌套对的数组。
2.系统初始化时钟中断(IRQ0)
①先看看start_kernel(\init\main.c)吧
IRQ0中断处理全过程
1:系统注册IRQ0(时钟中断)的下半部分的处理过程。
在\kernel\sched.c的sched_init函数中
init_bh(TIMER_BH, timer_bh);/*TIMER_BH==0*/
init_bh(TQUEUE_BH, tqueue_bh);/*TQUEUE_BH==2*/
run_old_timers();
run_timer_list();
}
④void run_old_timers(void)
{/*
*简单的遍历处理timer_table数组的列表项,
*如果定时器已经触发,就调用相关的函数。*/
struct timer_struct *tp;
unsigned long mask;
}
mark_bh()函数把bh_active相应的位置1.
这里看到了没有,其实时钟中断有两个下半部分。
一个TIMER_BH,一个TQUEUE_BH。
4现在来看看系统执行下半底过程。do_bottom_half
①do_bottom_half(\kernel\softirq.c)做一些处理后,就调用run_bottom_halves();
{
int status;
int cpu = smp_processor_id();
irq_enter(cpu, irq);
status = 1; /* Force the "do bottom halves" bit */
do {
if (!(action->flags & SA_INTERRUPT))
init_bh(IMMEDIATE_BH, immediate_bh);/*IMMEDIATE_BH==11*/
init_bh(TIMER_BH, timer_bh)把timer_bh函数注册为定时器的下半部分。
来看看init_bh是怎么处理的。
去掉一些加琐解琐的东西,就变成以下了。
void init_bh(int nr, void (*routine)(void))
irq_desc[i].depth = 0;
if (i < 16) {
irq_desc[i].handler = &i8259A_irq_type;
} else {
irq_desc[i].handler = &no_irq_type;
}
}
}
把IRQ小于16的handler都置为了i8259A_irq_type。
for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (time_after(tp->expires, jiffies))
③init_ISA_irqs()在同一个文件里
void init_ISA_irqs (void)
{ int i;
for (i = 0; i < NR_IRQS; i++) {
irq_desc[i].status = IRQ_DISABLED;
irq_desc[i].action = 0;
continue;
timer_active &= ~mask;
tp->fn();
sti();
}
}
老的系统定时器如下组织:
timer_table[32] timer_struct
Timer_struct
Timer_active
31 0
⑤run_timer_list()也差不多是这样组织的。
Timer_head timer_list timer_list
Struct irq_action
3当一个时钟中断(IRQ0)到来,系统是如何处理的
①触发IRQ0时,CPU跳到IRQ0X00_interrupt的程序。
类式汇编代码如下:
IRQ0X00_interrupt:
Pushl 0x00-256
Jmp common_interrupt
②common_interrup在\arch\i386\kernel\irq.h一定如下:
irq_desc[irq].handler->handle(irq, ®s);
if (1) {
if (bh_active & bh_mask)
do_bottom_half();/*处理下半部分*/
}
}
其实这里irq_desc[irq].handler->handle(irq, ®s);就是do_8259A_IRQ(irq,®
};
do_8259A_IRQ()是个重要的函数,是个IRQ响应处理程序。
同一个文件下,去掉那些不相关的部分,如下:
static void do_8259A_IRQ(unsigned int irq, struct pt_regs * regs)
{ struct irqaction * action;
#define BUILD_COMMON_IRQ() \
__asm__( \
"\n" __ALIGN_STR"\n" \
"common_interrupt:\n\t" \
SAVE_ALL \
"call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
"jmp ret_from_intr\n");
mark_bh(TIMER_BH);/*标记timer的下半部*/
if (!user_mode(regs))
lost_ticks_system++;/*如果在系统模式下
*在系统模式下丢失的滴答数加1
*/
if (tq_timer)/*判断定时器队列中有无任务*/
mark_bh(TQUEUE_BH);/*如果有,标记定时器队列的下半部分*/
rrupt().
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer",
NULL, NULL};
现在关于IRQ0的初始化完成了。变成如下图示:
irq_desc_t irq_desc[]
struct hw_interrupt_type
i8259A_irq_type定义如下:
static struct hw_interrupt_type i8259A_irq_type = {
"XT-PIC",
startup_8259A_irq,
shutdown_8259A_irq,
do_8259A_IRQ,
enable_8259A_irq,
disable_8259A_irq
⑤在do_timer_interrupt中又调用do_timer(\kernel\sched.c)。其他东西现在不感兴趣
。
⑥void do_timer(struct pt_regs * regs)
{
/*全局变量jiffies加1*/
(*(unsigned long *)&jiffies)++;
lost_ticks++;/*丢失的定时器滴答的数目*/
s),
对于IRQ0来说,它会间接的调用timer_interrupt(),上面已经讲过。
④timer_interrupt(\arch\i386\kernel\Time.c)是内核接收到IRQ0之后使用的函数,从
CPU时间截记数器中读取一些属性值。如果有值,就调用do_timer_interrup
t(同一个文件中)。
③do_IRQ在\arch\i386\kernel\irq.c
void do_IRQ(struct pt_regs regs)
{
int irq = regs.orig_eax & 0xff; /* subtle, see irq.h */
int cpu = smp_processor_id();
kstat.irqs[cpu][irq]++;/*更新内核的一些统计数字*/
if (status & SA_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
__cli();
irq_exit(cpu, irq);
return status;
}
只注意这里一个函数:
action->handler(irq, action->dev_id, regs);
unsigned long bh_mask = 0;
void (*bΒιβλιοθήκη Baidu_base[32])(void);
bh_base[]
31 bh_active 0
Bottom half handler(timer_bh)
31 bh_mask 0
不好意思,画的这么难看:P
如果bh_mask的第N位被置为1,则表明bh_base[]中的第N个指针指向了一个Bottom half
有几个跟时钟中断(IRQ0)相关的函数。
init_IRQ();
sched_init();
time_init();
sched_init()就是注册时钟中断的下半部分,其实IRQ0两个下半部分,一个前面已经看
到,还有一个下半部分init_bh(TQUEUE_BH, tqueue_bh);下面会看到。
②init_IRQ函数在\arch\i386\kernel\irq.c
do {
if (active & 1)
(*bh)();
bh++;
active >>= 1;
} while (active);
}
③对于IRQ0来说,(*bh)();就是timer_bh(\kernel\Sched.c);
void timer_bh(void)
{/*这是定时器真正的下半部分*/
update_times();/*更新统计数字*/
__sti();/*如果没有设置SA_INTERRUPT标记,就允许中断*/
else
__cli();
status |= action->flags;
action->handler(irq, action->dev_id, regs);
action = action->next;
} while (action);/*多个action只有在这个IRQ共享的情况下*/
对于IRQ0,其实就是timer_interrupt函数。
不,其实现在还不是,等time_init()完就基本差不多了。
⑤time_init()在\arch\i386\kernel\Time.c中
感兴趣的就是setup_x86_irq(0, &irq0);作用就是给IRQ0增加一个操作。即timer_inte
irq_desc_t *desc = irq_desc + irq;
{if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
action = desc->action;
status |= IRQ_INPROGRESS;
}
desc->status = status;
{
bh_base[nr] = routine;
atomic_set(&bh_mask_count[nr], 0);
bh_mask |= 1 << nr;
}
就是简单的设置bh_base和bh_mask.
看看这些的定义:
atomic_t bh_mask_count[32];
unsigned long bh_active = 0;
⑥时钟中断的另外一个下半底tqueue_bh(\kernel\Sched.c)
}
handle_IRQ_event(irq, regs, action);
}
④handle_IRQ_event()也在同一个文件里。
int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqacti
on * action)
②void run_bottom_halves(void)
{
unsigned long active;
void (**bh)(void);
active = get_active_bhs();/*取得激活的下半底*/
clear_active_bhs(active);/*清那些激活的下半底*/
bh = bh_base;
例程。
如果bh_active的第N位被置为1,则表明一旦调度进程许可,立即调用第N个Bottom hal
f例程。
bh_mask_count[]跟踪为每个下半部分提出的enable/disable请求嵌套对的数组。
2.系统初始化时钟中断(IRQ0)
①先看看start_kernel(\init\main.c)吧
IRQ0中断处理全过程
1:系统注册IRQ0(时钟中断)的下半部分的处理过程。
在\kernel\sched.c的sched_init函数中
init_bh(TIMER_BH, timer_bh);/*TIMER_BH==0*/
init_bh(TQUEUE_BH, tqueue_bh);/*TQUEUE_BH==2*/
run_old_timers();
run_timer_list();
}
④void run_old_timers(void)
{/*
*简单的遍历处理timer_table数组的列表项,
*如果定时器已经触发,就调用相关的函数。*/
struct timer_struct *tp;
unsigned long mask;
}
mark_bh()函数把bh_active相应的位置1.
这里看到了没有,其实时钟中断有两个下半部分。
一个TIMER_BH,一个TQUEUE_BH。
4现在来看看系统执行下半底过程。do_bottom_half
①do_bottom_half(\kernel\softirq.c)做一些处理后,就调用run_bottom_halves();
{
int status;
int cpu = smp_processor_id();
irq_enter(cpu, irq);
status = 1; /* Force the "do bottom halves" bit */
do {
if (!(action->flags & SA_INTERRUPT))
init_bh(IMMEDIATE_BH, immediate_bh);/*IMMEDIATE_BH==11*/
init_bh(TIMER_BH, timer_bh)把timer_bh函数注册为定时器的下半部分。
来看看init_bh是怎么处理的。
去掉一些加琐解琐的东西,就变成以下了。
void init_bh(int nr, void (*routine)(void))
irq_desc[i].depth = 0;
if (i < 16) {
irq_desc[i].handler = &i8259A_irq_type;
} else {
irq_desc[i].handler = &no_irq_type;
}
}
}
把IRQ小于16的handler都置为了i8259A_irq_type。
for (mask = 1, tp = timer_table+0 ; mask ; tp++,mask += mask) {
if (mask > timer_active)
break;
if (!(mask & timer_active))
continue;
if (time_after(tp->expires, jiffies))