又是中断(三)-初始化

 中断的初始化
要使用中断肯定得初始化,这些初始化在系统启动时已经为你做好了,但是我们还是来看看怎样初始化的,这样就能更好的理解中断机制了。
先看下面函数:

355 void __init init_ISA_irqs (void)
356 
{
357 
int i;
358 
//省略了一些代码

362 init_8259A
(0);
363 
364 
for (= 0; i < NR_IRQS; i++) {
365 irq_desc
[i].status = IRQ_DISABLED;
366 irq_desc
[i].action = NULL;
367 irq_desc
[i].depth = 1;
368 
369 
if (< 16) {
370 
/*
371 * 16 old-style. INTA-cycle interrupts:
372 */

373 set_irq_chip_and_handler_name
(i, &i8259A_chip,
374 handle_level_irq
, "XT");
375 
} else {
376 
/*
377 * 'high' PCI IRQs filled in on demand
378 */

379 irq_desc
[i].chip = &no_irq_chip;
380 
}
381 
}
382 
}


上面的函数分为两个部分,一个是init_8259A(0),另一个就是for循环了。从名字上我们就可以看出init_8259A()是干什么的了,它是用来初始化8259A的,设置它的相关寄存器,指明了它的工作方式。下面的for循环,一共循环了NR_IRQS(224)次,将刚才所讲的irq_desc[]初始化,初始化了status,action,depth字段,然后如果是前16个调用set_irq_chip_and_handler_name(),再往下看如果大于16,那么给字段chip赋no_irq_chiq这个定义如下 (kernel/irq/handle.c):

88 struct irq_chip no_irq_chip = { 
 89 
.name = "none",
 90 
.startup = noop_ret,
 91 
.shutdown = noop,
 92 
.enable = noop,
 93 
.disable = noop,
 94 
.ack = ack_bad,
 95 
.end = noop,
 96 
};

也就是说大于16是不需要中断控制器的。
然后再来看函数set_irq_chip_and_handler_name(),设置irq_desc[]数组中的chip和handle_irq字段,其函数定义如下:


594 set_irq_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
595 irq_flow_handler_t handle
, const char *name)
596 
{
597 set_irq_chip
(irq, chip);//设置字段chip
598 __set_irq_handler(irq, handle, 0, name);//设置字段handler
599 } 

函数set_irq_chip()中的主要代码为(kernel/irq/chip.c):

int set_irq_chip(unsigned int irq, struct irq_chip *chip)

struct irq_desc *desc;
desc 
= irq_desc + irq;//找到所要初始化的irq_desc

desc
->chip = chip;

函数__set_irq_handler()定义如下(kernel/irq/chip.c):

void
 __set_irq_handler
(unsigned int irq, irq_flow_handler_t handle, int is_chained, 
                   
const char *name)
{
struct irq_desc *desc;
desc 
= irq_desc + irq;
desc
->handle_irq = handle;//给字段handle_irq赋一个函数

desc
->name = name;


也就是说执行set_irq_chip_and_handler_name(i, &i8259A_chip,handle_level_irq, "XT")后给数组irq_desc[]的第i个字段chip,handle_irq,name分别赋值i8259A_chip,handle_level_irq,XT。而其中handle_level_irq是一个函数指针,定义在kernel/irq/chip.c重要代码如下:

void fastcall
handle_level_irq
(unsigned int irq, struct irq_desc *desc)
{
struct irqaction *action;
irqreturn_t action_ret
;
     action 
= desc->action;
     action_ret 
= handle_IRQ_event(irq, action);/* 注意这个函数*/
}

至此init_ISA_irqs (void)到这里结束,那么谁来调用它了?看下面代码:

387 void __init native_init_IRQ(void)
388 
{
389 
int i;
//...

392 pre_intr_init_hook
();
//...

399 
for (= 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
400 
int vector = FIRST_EXTERNAL_VECTOR + i;
401 
if (>= NR_IRQS)
402 
break;
403 
if (vector != SYSCALL_VECTOR) 
404 set_intr_gate
(vector, interrupt[i]);
405 
}
420 
} 

其中将392行的函数pre_intr_init_hook()展开后就会发现:

30 void __init pre_intr_init_hook(void)
 31 
{
 32 init_ISA_irqs
();
 33 
}

也就是在函数native_init_IRQ()里调用了init_ISA_irqs (void)。接着看native_init_IRQ()中的for循环,循环了224次(NR_VECTORS - FIRST_EXTERNAL_VECTOR),vector从0x20(FIRST_EXTERNAL_VECTOR)开始。且不等于0x80(vector != SYSCALL_VECTOR),即系统调用,循环执行set_intr_gate(vector, interrupt[i]),这个函数就是设置IDT中的中断门描述符(前面讲了,占8字节,主要保存中断处理程序的函数地址),将中断处理函数入口函数地址设置为interrupt[i],当第i个中断发生后跳转到此地址执行。那么interrupt[]数组又是怎么实现的了?

69 #define IRQ(x,y) /
 70 IRQ
##x##y##_interrupt
 71 
 72 
#define IRQLIST_16(x) /
 73 IRQ
(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), /
 74 IRQ
(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), /
 75 IRQ
(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), /
 76 IRQ
(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
 77 
 78 
/* for the irq vectors */
 79 
static void (*interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
 80 IRQLIST_16
(0x2), IRQLIST_16(0x3),
 81 IRQLIST_16
(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
 82 IRQLIST_16
(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
 83 IRQLIST_16
(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
 84 
};


先看79行,这个就是函数指针数组interrupt[],注意这个数组共224项,定义了从0x2到0xf的14个IRQLIST_16(),而每一个IRQLIST_16()又是16个IRQ(x,y),也就是说一共有224个IRQ(x,y)。
接着再看宏#define IRQ(x,y) IRQ##x##y##_interrupt
##表示将字符串连接起来,比如IRQ(0x2,0)就是IRQ0x20_interrupt,这样以来就会生成224个这样的函数,从IRQ0x20_interrupt一直到IRQ0xff_interupt。那么这些函数是如何定义的了?往下看include/asm-x86_64/hw_irq.h:


156 #define IRQ_NAME2(nr) nr##_interrupt(void)
157 
#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) 

163 
#define BUILD_IRQ(nr) /
164 asmlinkage 
void IRQ_NAME(nr); /
165 
__asm__( /
166 
"/n.p2align/n" /
167 
"IRQ" #nr "_interrupt:/n/t" /
168 
"push $~(" #nr ") ; " /
169 
"jmp common_interrupt");


根据156和157两行的宏定义可以知道IRQ_NAME(nr)是一个函数IRQnr_interrupt()就是我们刚才上面所提到的函数数组中interrupt[]的一个,至于是哪一个就看nr是多少了。这个函数将nr取反(正数留给系统调用)压入堆栈中,后面用到的时候再取反就可以知道中断号了,然后跳转到common_interrupt,这是一个公共的程序,暂且不说。
这样我们就定义了一个interrupt[]数组中的一个函数,这只是一个函数,通过以下的宏就可以定义224个了arch/x86_64/kernel/i8259.c:


60 BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
 61 BUILD_16_IRQS
(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
 62 BUILD_16_IRQS
(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
 63 BUILD_16_IRQS
(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)


这样以后就定义整个interrupt[]中的每一函数。
至此set_intr_gate(vector, interrupt[i]);执行结束。这样我们就对IDT初始化成功。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值