中断处理 - 常见错误
错误 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