X86在32bit保护模式和64bit的长模式下的中断处理依靠IDT,中断描述符表。
这个表是X86众多描述符表的一个,每一个表项都是一个中断门/陷阱门/任务门描述符,用来描述该表项对应的中断向量的处理。每个表项长度8Byte。
X86一共有256个向量,其中包括了中断和异常。前32个向量是X86体系架构预先定义好的,都有明确的含义,例如,向量2预留给NMI,向量14用于Page Fault,向量18预留给Machine Check异常等等。
IDT的操作需要依靠IDTR寄存器和两个指令(LIDT/SIDT),关系图请参考SDM 图6-1。
除了前32个预留的向量,其余的向量都是怎么用的呢?这个会根据系统不同而不同,例如Linux之前的系统调用是通过INT 80H来完成的,因此可以判断在向量0x80对应的描述符一定是指向了系统调用的入口。
下面就是在Linux-3.2.33系统当中dump出来部分的IDT表项。
(E4)[ 74895.380487] -------- test start ---------
(E4)[ 74895.380493] XXXXXXX idt.address = 0x8169e000,idt.size=0xfff. Gate Size=0x10
(E4)[ 74895.380495] === 0x00: ffffffff8169e000 ===
(E4)[ 74895.380497] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FE40. (divide_error+0x0/0x20)
(E4)[ 74895.380509] === 0x01: ffffffff8169e010 ===
(E4)[ 74895.380511] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81387D70. (debug+0x0/0x40)
(E4)[ 74895.380518] === 0x02: ffffffff8169e020 ===
(E4)[ 74895.380520] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF813880B0. (nmi+0x0/0x20)
(E4)[ 74895.380525] === 0x03: ffffffff8169e030 ===
(E4)[ 74895.380526] Interrupt gate. Present: 001. DPL: 003.Offset = 0xFFFFFFFF81387DB0. (int3+0x0/0x40)
(E4)[ 74895.380531] === 0x04: ffffffff8169e040 ===
(E4)[ 74895.380533] Interrupt gate. Present: 001. DPL: 003.Offset = 0xFFFFFFFF8138FE60. (overflow+0x0/0x20)
(E4)[ 74895.380538] === 0x05: ffffffff8169e050 ===
(E4)[ 74895.380539] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FE80. (bounds+0x0/0x20)
(E4)[ 74895.380544] === 0x06: ffffffff8169e060 ===
(E4)[ 74895.380545] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FEA0. (invalid_op+0x0/0x20)
(E4)[ 74895.380550] === 0x07: ffffffff8169e070 ===
(E4)[ 74895.380552] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FEC0. (device_not_available+0x0/0x20)
(E4)[ 74895.380557] === 0x08: ffffffff8169e080 ===
(E4)[ 74895.380558] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FEE0. (double_fault+0x0/0x30)
(E4)[ 74895.380563] === 0x09: ffffffff8169e090 ===
(E4)[ 74895.380564] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FF10. (coprocessor_segment_overrun+0x0/0x20)
(E4)[ 74895.380570] === 0x0a: ffffffff8169e0a0 ===
(E4)[ 74895.380571] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FF30. (invalid_TSS+0x0/0x30)
(E4)[ 74895.380576] === 0x0b: ffffffff8169e0b0 ===
(E4)[ 74895.380577] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FF60. (segment_not_present+0x0/0x30)
(E4)[ 74895.380582] === 0x0c: ffffffff8169e0c0 ===
(E4)[ 74895.380584] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81387DF0. (stack_segment+0x0/0x30)
(E4)[ 74895.380589] === 0x0d: ffffffff8169e0d0 ===
(E4)[ 74895.380590] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81387E20. (general_protection+0x0/0x30)
(E4)[ 74895.380595] === 0x0e: ffffffff8169e0e0 ===
(E4)[ 74895.380596] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81387E50. (page_fault+0x0/0x30)
(E4)[ 74895.380602] === 0x0f: ffffffff8169e0f0 ===
(E4)[ 74895.380603] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FF90. (spurious_interrupt_bug+0x0/0x20)
(E4)[ 74895.380608] === 0x10: ffffffff8169e100 ===
(E4)[ 74895.380610] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FFB0. (coprocessor_error+0x0/0x20)
(E4)[ 74895.380615] === 0x11: ffffffff8169e110 ===
(E4)[ 74895.380616] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138FFD0. (alignment_check+0x0/0x30)
(E4)[ 74895.380621] === 0x12: ffffffff8169e120 ===
(E4)[ 74895.380622] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81387E80. (machine_check+0x0/0x20)
(E4)[ 74895.380627] === 0x13: ffffffff8169e130 ===
(E4)[ 74895.380629] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF81390000. (simd_coprocessor_error+0x0/0x20)
(E4)[ 74895.380634] === 0x14: ffffffff8169e140 ===
(E4)[ 74895.380635] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0C8. (early_idt_handlers+0xc8/0x140)
(E4)[ 74895.380645] === 0x15: ffffffff8169e150 ===
(E4)[ 74895.380647] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0D2. (early_idt_handlers+0xd2/0x140)
(E4)[ 74895.380652] === 0x16: ffffffff8169e160 ===
(E4)[ 74895.380654] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0DC. (early_idt_handlers+0xdc/0x140)
(E4)[ 74895.380659] === 0x17: ffffffff8169e170 ===
(E4)[ 74895.380661] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0E6. (early_idt_handlers+0xe6/0x140)
(E4)[ 74895.380666] === 0x18: ffffffff8169e180 ===
(E4)[ 74895.380667] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0F0. (early_idt_handlers+0xf0/0x140)
(E4)[ 74895.380673] === 0x19: ffffffff8169e190 ===
(E4)[ 74895.380674] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA0FA. (early_idt_handlers+0xfa/0x140)
(E4)[ 74895.380680] === 0x1a: ffffffff8169e1a0 ===
(E4)[ 74895.380681] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA104. (early_idt_handlers+0x104/0x140)
(E4)[ 74895.380687] === 0x1b: ffffffff8169e1b0 ===
(E4)[ 74895.380688] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA10E. (early_idt_handlers+0x10e/0x140)
(E4)[ 74895.380694] === 0x1c: ffffffff8169e1c0 ===
(E4)[ 74895.380695] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA118. (early_idt_handlers+0x118/0x140)
(E4)[ 74895.380700] === 0x1d: ffffffff8169e1d0 ===
(E4)[ 74895.380702] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA122. (early_idt_handlers+0x122/0x140)
(E4)[ 74895.380707] === 0x1e: ffffffff8169e1e0 ===
(E4)[ 74895.380708] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA12C. (early_idt_handlers+0x12c/0x140)
(E4)[ 74895.380714] === 0x1f: ffffffff8169e1f0 ===
(E4)[ 74895.380715] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF815DA136. (early_idt_handlers+0x136/0x140)
(E4)[ 74895.380721] === 0x20: ffffffff8169e200 ===
(E4)[ 74895.380722] Interrupt gate. Present: 001. DPL: 000.Offset = 0xFFFFFFFF8138EB00. (irq_move_cleanup_interrupt+0x0/0x70)
。。。。
(E4)[ 74895.381495] === 0x80: ffffffff8169e800 ===
(E4)[ 74895.381498] Interrupt gate. Present: 001. DPL: 003.Offset = 0xFFFFFFFF81390440. (ia32_syscall+0x0/0x50)
。。。。
(E4)[ 74895.382662] Interrupt gate. Present: 001. DPL: 000.
Offset = 0xFFFFFFFF8138FD60. (spurious_interrupt+0x0/0x70)
可见80H的IDT表项果然指向了ia32_syscall。Linux也是“诚不我欺也!”
X86的中断处理还是很复杂的,它涉及到了体系结构以及PIC(中断控制器)的进化。这里一句半句也说不明白,有机会再具体描述吧。有需要的还是读读SDM吧,里面应有尽有。
Linux里面可以查看特定中断发生的统计信息byreading /proc/interrupts。
从体系结构上来看,中断/异常处理最简单是PowerPC(PPC603E的核心)。它有256个中断向量,每个向量预留了0x100字节。reset向量是第一个,开始于0x100,上电初始化的代码就放在这里)。第二个向量开始于0x200,依此类推。
PowerPC预留了256字节给每一个中断向量,这是一个非常巧妙的而且简单直接的设计。对于简单的操作,256直接足够放下一个中断/异常处理函数。对于复杂的中断/异常处理,256字节也足够完成跳转操作。中断处理也是非常简洁。
而且PowerPC的通用寄存器有32个,因此ABI也是简单明了。它是big-endian的,寄存器的bit设置符合通常软件工程师的思维常理。
所以PowerPC是学习CPU相关的底层软件的好平台。
可惜现在PowerPC没落了。
为什么技术强的产品都没有坚持到最好,摄影界的Minolta,汽车里的雪铁龙,电脑里的DEC,软件里的Boland/Solaris……
唉,不说了。