- 410 for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
-  411 int vector = FIRST_EXTERNAL_VECTOR + i;
-  412 if (i >= NR_IRQS)
-  413 break;
-  414 if (vector != SYSCALL_VECTOR) 
-  415 set_intr_gate(vector, interrupt[i]);
- 416 }
- 540 .data
-  541 ENTRY(interrupt)
-  542 .text
-  543 
-  544 vector=0
-  545 ENTRY(irq_entries_start)
-  546 RING0_INT_FRAME
-  547 .rept NR_IRQS
-  548 ALIGN
-  549 .if vector
-  550 CFI_ADJUST_CFA_OFFSET -4
-  551 .endif
-  552 1: pushl $~(vector)
-  553 CFI_ADJUST_CFA_OFFSET 4
-  554 jmp common_interrupt
-  555 .data
-  556 .long 1b
-  557 .text
-  558 vector=vector+1
- 559 .endr
上面的代码开起来很乱,我们整理一下,使其更加易读,但是又不失其本质。
- .data
-  ENTRY(interrupt)
- .long 1b //注意 如果把数据段放在这里那么1b要改为1f了,而且失去循环执行NR_IRQS次的功能。 //我们只能人为的把他们想象成被循环执行了NR_IRQS次 :)
-  .text
-  vector=0
-  ENTRY(irq_entries_start)
-  .rept NR_IRQS
-  1: pushl $~(vector)
-          jmp common_interrupt
- vector=vector+1
我们把数据段放在一起,代码段放在一起。注意这里的代码段是用来初始化数据段的。
我们看到:interrupt作为一个内存标签,其内容为代码段标号1所表示的地址。同时我们也注意到这个interrupt数组的所有项的内容都是一样的:全部为“标号1”的符号地址。
每次外部中断来临时,硬件自动根据PIC或者APIC送出来的中断类型码(中断向量号)去查找中断描述符表的相应项,然后得到interrupt[n]的内容,继而转去执行标号1地址的代码,而标号1的代码仅将中断号取反后压入栈中,后立马跳到common_interupt标号处去执行。至于为什么要把中断向量号取反后压栈,则是由于内核用正的相应号去表示系统调用号,用负号来表示中断号。
细心的你有可能发现了一个问题:上述代码片段只是向interrupt表示的内存地址处存入大量重复的4字节数据,却没有象C语言定义数组那样定义interrupt[NR_IRQS-1],那么set_intr_gate这样的函数是如何确切的指导interrupt是一个数组呢,而且数组的内容是函数指针?
例如:set_intr_gate(vector,interrupt[i]);是如何取interrupt[i]内容呢?
啰嗦了这么半天...
如下一个声明就搞定了。
[])(void);//函数指针数组- 566 common_interrupt:
-  567 SAVE_ALL
-  568 TRACE_IRQS_OFF
-  569 movl %esp,%eax
-  570 call do_IRQ
-  571 jmp ret_from_intr
- 572 CFI_ENDPROC
common_interrupt首先执行宏SAVE_ALL保存中断处理程序可能用到的寄存器(注意:cs eip ss esp由硬件自动保存)然后把栈顶指针传给eax寄存器后(eax寄存器内容将作为do_IRQ函数的参数)调用do_IRQ进行中断处理,返回后
执行ret_from_intr从中断返回。
