中断处理 - 常见错误

错误 1:Triple Fault(三重错误重启)

症状

QEMU不断重启,无法看到任何输出。

原因

IDT配置错误 → 异常发生 → 处理异常时再次出错 → 双重错误 → 处理双重错误再出错 → Triple Fault → CPU重置

解决方案

# 使用QEMU日志查看发生了什么
qemu-system-x86_64 -fda os-image.bin -d int,cpu_reset -no-reboot
  • 检查IDT条目的段选择子是否为0x08(代码段)
  • 检查标志位是否为0x8E
  • 确保lidt指令使用了正确的IDT指针

错误 2:中断后系统挂死

症状

第一个中断处理正常,之后系统不再响应。

原因

忘记发送EOI(End of Interrupt)给PIC。

解决方案

void irq_handler(registers_t *regs) {
    // 必须在处理完中断后发送EOI!
    if (regs->int_no >= 40) {
        outb(0xA0, 0x20);  // 从PIC EOI
    }
    outb(0x20, 0x20);      // 主PIC EOI
}

错误 3:pusha/popa顺序错误

症状

从中断返回后寄存器值混乱,随机崩溃。

原因

保存和恢复寄存器的顺序不匹配。

解决方案

确保汇编中的保存顺序与C结构体匹配:

; 保存(低地址→高地址对应结构体从上到下)
pusha          ; edi, esi, ebp, esp, ebx, edx, ecx, eax
push ds        ; 在最低地址
 
; 恢复(相反顺序)
pop ds
popa
add esp, 8     ; 跳过中断号和错误码
iret

错误 4:段选择子未切换

症状

在中断处理程序中访问数据时出现通用保护错误。

原因

中断可能发生在任何时刻,某些段寄存器可能不指向内核数据段。

解决方案

isr_common_stub:
    pusha
    mov ax, ds
    push eax        ; 保存原DS
    
    mov ax, 0x10    ; 内核数据段选择子
    mov ds, ax      ; 切换到内核数据段
    mov es, ax
    mov fs, ax
    mov gs, ax
    ; ... 调用C处理程序 ...

错误 5:PIC重映射不工作

症状

时钟中断(IRQ0)触发了除零错误处理程序。

原因

PIC未重映射,IRQ0仍然映射到向量0(除零异常)。

解决方案

  • 确保在设置IRQ的IDT条目之前调用pic_remap()
  • 验证ICW序列(ICW1→ICW2→ICW3→ICW4)完整发送
  • 检查端口号:主PIC (0x20/0x21),从PIC (0xA0/0xA1)

错误 6:栈帧对齐问题

症状

C处理程序接收到错误的寄存器值。

原因

有错误码和无错误码的中断混用了同一个宏。

解决方案

CPU异常中有错误码的向量:8, 10, 11, 12, 13, 14, 17, 21 其余所有异常无错误码——必须手动压入0占位。

调试技巧

使用GDB检查IDT

(gdb) # 查看IDT内容
(gdb) x/32x &idt   # 查看前几个IDT条目

# 在中断处理程序入口设断点
(gdb) break isr_common_stub
(gdb) break irq_handler

使用QEMU跟踪中断

# 显示所有中断
qemu-system-x86_64 -fda os-image.bin -d int
 
# 只显示中断,不显示CPU异常
qemu-system-x86_64 -fda os-image.bin -d int -D qemu.log