pre_intr_init_hook() 调用 init_ISA_irqs(),初始化 irq_desc 数组、status、action、depth。
在循环中,对于所有在 FIRST_EXTERNAL_VECTOR(0x20) 与 NR_VECTOR(0x100)之间的、不是系统中断的 256 - 32 - 1 = 223 项,调用 set_intr_gate(),初始化为中断门。
现在我们关心的是,这些中断门的中断处理程序是什么?在 x86 体系结构下没找到 interrupt 数组的定义,因此使用 64 位体系结构的做说明:// arch/x86/kernel/i8259_64.c
80 static void (*__initdata interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
81 IRQLIST_16(0x2), IRQLIST_16(0x3),
82 IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
83 IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
84 IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
85 };
以上是 interrupt 数组的定义。在下面的代码中,## 是将字符串连接起来,这样宏定义的函数 IRQ(0x4,6) 就是 IRQ0x46_interrupt,生成 224 个这样的函数填入数组。
// arch/x86/kernel/i8259_64.c
70 #define IRQ(x,y) \
71 IRQ##x##y##_interrupt
72
73 #define IRQLIST_16(x) \
74 IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
75 IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
76 IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
77 IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
那么这 224 个函数在哪里呢?通过下面的宏可以生成一个汇编函数,它调用了 common_interrupt 函数:
// include/asm/hw_irq_64.h
155 #define IRQ_NAME2(nr) nr##_interrupt(void)
156 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
162 #define BUILD_IRQ(nr) \
163 asmlinkage void IRQ_NAME(nr); \
164 asm("\n.p2align\n" \
165 "IRQ" #nr "_interrupt:\n\t" \
166 "push $~(" #nr ") ; " \
167 "jmp common_interrupt");
common_interrupt 是汇编函数,这个函数最终调用了 do_IRQ,这是我们下章要介绍的核心中断处理函数。
// arch/x86/kernel/entry_32.S
613 common_interrupt:
614 SAVE_ALL
615 TRACE_IRQS_OFF
616 movl %esp,%eax
617 call do_IRQ
618 jmp ret_from_intr
619 ENDPROC(common_interrupt)
看来,只需要调用 BUILD_IRQ 就能生成中断处理函数了。Linux Kernel 正是这样做的:
// arch/x86/kernel/i8259_64.c
37 #define BI(x,y) \
38 BUILD_IRQ(x##y)
39
40 #define BUILD_16_IRQS(x) \
41 BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
42 BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
43 BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
44 BI(x,c) BI(x,d) BI(x,e) BI(x,f)
....................................................
61 BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
62 BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
63 BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
64 BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)
不得不发表一下感慨,Linux Kernel 对 C 语言宏的运用真是炉火纯青。如果是我写代码,很可能不得不先写个代码生成器,用它生成 224 个函数的源码。但 Linux Kernel Source 里出现几百个几乎一模一样的函数太不优雅了,于是利用 C 语言的预处理机制,采用二级宏定义,尽可能降低代码量。