(IDT)中断向量表 📅 2026/8/5 3:03:50 中断向量表Interrupt Descriptor Table, IDT是x86/x64架构中一个极其核心的数据结构。它是CPU处理外部硬件中断、内部异常和软件中断的“索引目录”。简单来说IDT是一个存储在内存中的数组其每个条目都是一个门描述符Gate Descriptor告诉CPU当某个中断或异常发生时该跳转到内核中的哪个地址去执行处理程序。一、IDT的作用与地位硬件中断如键盘按下IRQ1、定时器触发IRQ0等通过IDT找到对应的设备驱动处理函数。处理器异常如除零错误#DE向量0、缺页异常#PF向量14等通过IDT找到内核的异常处理函数。软件中断通过int $0x80指令触发的系统调用通过IDT陷入内核。IDT与GDT的区别GDT全局描述符表定义内存段代码段、数据段的属性。IDT中断描述符表定义中断/异常的处理入口。二、IDT的存储IDTR寄存器CPU通过一个专门的寄存器IDTRInterrupt Descriptor Table Register来定位IDT。IDTR80位x86_64 ------------------------------------ | Limit (16位) | Base (64位) | ------------------------------------BaseIDT在内存中的线性基地址。LimitIDT的大小字节数减1。例如如果IDT有256个条目每个条目16字节则Limit 256 * 16 - 1 4095。加载IDT的指令lidt [idtr_addr]特权指令需在ring 0执行。三、IDT条目的结构x86_64在64位长模式下每个IDT条目大小为16字节128位称为中断门Interrupt Gate或陷阱门Trap Gate其结构如下-------------------------------------------------------------------------- | Offset 15:0 (2B) | Segment Sel (2B) | IST (3b) | 类型 (5b) | DPL (2b) | P (1b) | -------------------------------------------------------------------------- | Offset 31:16 (2B) | Offset 63:32 (4B) | 保留 (4B) | --------------------------------------------------------------------------关键字段解释字段位宽含义Offset64位处理函数的入口地址RIP。Segment Selector16位处理函数所在的代码段选择子通常是__KERNEL_CS即内核代码段。IST3位中断栈表Interrupt Stack Table索引。非0时CPU会切换到TSS中指定的IST栈用于NMI、#DF等关键异常。Type5位门类型0xE中断门自动清IF、0xF陷阱门不清IF。DPL2位描述符特权级0~3。DPL3时用户态ring 3可通过int触发如int 0x80。PPresent1位1表示该门有效0表示无效触发#GP。四、中断门 vs 陷阱门的区别类型Type值IF中断标志位行为适用场景中断门Interrupt Gate0xE1110自动清0屏蔽后续中断硬件中断处理防止中断嵌套混乱。陷阱门Trap Gate0xF1111不改变IF保持原样系统调用、调试异常允许中断嵌套。五、IDT条目示例汇编定义; 定义IDT条目64位中断门 ; 假设处理函数地址为 handler idt_entry: dw (handler 0xFFFF) ; 偏移低16位 dw KERNEL_CS_SEL ; 段选择子内核代码段 db 0 ; 保留必须为0 db 0x8E ; P1, DPL0, Type0xE中断门 dw (handler 16) 0xFFFF ; 偏移中16位 dd (handler 32) 0xFFFFFFFF ; 偏移高32位 dd 0 ; 保留六、IDT的初始化流程以x86_64 Linux为例分配IDT内存在内存中分配一片256 * 16 4KB的连续内存。填充IDT条目为每个中断/异常设置处理函数入口、段选择子、类型等。加载IDTR使用lidt指令将IDT的基地址和大小写入IDTR。启用中断执行sti设置中断标志位允许CPU响应外部中断。Linux内核中的相关代码// arch/x86/kernel/idt.c void __init idt_init(void) { // 初始化各个IDT条目例如系统调用门int 0x80 set_intr_gate(0x80, entry_INT80_compat); // 加载IDT load_idt(idt_descr); }七、常见陷阱与注意事项1. IST中断栈表的使用如果某个IDT条目设置了IST字段非0CPU在处理该中断时会自动切换到TSS中指定的IST栈而不是使用当前栈。这主要用于NMI不可屏蔽中断防止在普通中断处理中再次触发NMI时栈冲突。#DF双重故障当处理一个异常时又发生另一个异常系统崩溃前需要一个干净栈来记录错误信息。2. DPL的作用DPL0只有内核态ring 0才能触发该中断/异常。DPL3用户态ring 3也可以通过int指令触发例如系统调用int 0x80。注意如果用户态尝试触发DPL0的中断门会触发#GP通用保护异常。3. 中断门自动清IF的影响中断门会在进入处理程序时自动清除IF中断标志位这意味着在处理该中断期间CPU不会响应外部中断除非手动sti。陷阱门则保持IF不变允许中断嵌套。八、IDT与系统调用int 0x80在传统的x86 Linux系统调用中int 0x80指令通过IDT的第128号条目0x80进入内核。用户态 (ring 3): int 0x80 ↓ CPU查找IDT[0x80]门描述符DPL3 ↓ 检查权限CPL3, DPL3 → 允许进入 ↓ 切换到内核栈压入SS、RSP、RFLAGS、CS、RIP ↓ 跳转到 system_call 处理函数位于内核代码段 ↓ 执行系统调用... ↓ iretq 返回用户态九、总结概念作用IDT中断描述符表定义所有中断/异常的处理入口地址。IDTR寄存器存放IDT的基地址和大小限制。中断门0xE自动关闭中断适用于硬件中断。陷阱门0xF保持中断状态适用于系统调用/调试。DPL描述符特权级控制哪些特权级可以触发该中断。IST中断栈表关键异常使用独立栈防止栈破坏。