你的 3 / 0 触发了 除零异常(Divide Error, #DE),这会导致 CPU 产生 0 号中断,从而调用 IDT(中断描述符表)中的异常处理程序。
1. 为什么 3 / 0 会调用 exception_handler ?
在 x86 架构中,除零异常(Divide Error, #DE) 是 CPU 自动触发的异常,编号是 0x00。
当 3 / 0 发生时,CPU 会执行以下步骤:
- 检测到除零错误。
- 触发
0x00号中断。 - 查找
IDT(中断描述符表),找到对应的异常处理程序地址。 - 跳转到
exception_handler_0(可能是exception_handler_divide_error)执行异常处理。
2. exception_handler 宏是如何工作的?
你的 宏 exception_handler 负责生成多个异常处理函数,用于处理各种 CPU 异常,例如:
exception_handler divide_error 0 0
exception_handler invalid_opcode 6 0
exception_handler general_protection 13 1
这会展开为:
exception_handler_divide_error:
pusha
push $0 ; 无错误码的异常,手动压入 0
push $0 ; 异常号 0
pushal
push %ds
push %es
push %fs
push %gs
push %esp
call do_handler_divide_error ; 调用 C 处理函数
pop %esp
pop %gs
pop %fs
pop %es
pop %ds
popal
add $(2*4), %esp ; 调整栈,跳过异常号和错误码
iret
(1) pusha 保存所有寄存器
pusha压入eax, ecx, edx, ebx, esp, ebp, esi, edi,防止中断处理程序破坏寄存器。
(2) push $0
push $0是因为 除零异常 没有错误码,但我们希望所有异常的栈结构一致,因此手动压入0。
(3) push $0
- 这是异常号(Divide Error =
0x00),用于在 C 语言do_handler_divide_error中区分异常类型。
(4) call do_handler_divide_error
- 调用 C 语言的异常处理函数,例如:
void do_handler_divide_error(uint32_t esp) { printf("Divide Error Exception Occurred!\n"); while (1); }
(5) iret 让 CPU 恢复中断前状态
iret(中断返回指令) 会 恢复CS:EIP并继续执行异常发生前的代码。- 如果不调用
iret,CPU 可能崩溃或死机。
3. IDT 是如何连接 exception_handler 的?
在内核初始化时,代码通常会为 IDT(中断描述符表)填充异常处理程序地址:
set_idt_gate(0, (uint32_t)exception_handler_divide_error, 0x08, 0x8E);
解释:
0→ 0 号中断(除零异常)exception_handler_divide_error→ 0 号中断的处理函数0x08→ 代码段选择子0x8E→ 中断门描述符,DPL=0(内核模式)
当 CPU 触发 0x00 号异常,它会:
- 跳转到
IDT[0]指定的exception_handler_divide_error。 - 执行异常处理。
- 如果
iret之后 CPU 仍然无法继续,则可能进入kernel panic。
4. 代码执行流程总结
int a = 3 / 0;发生 除零异常。- CPU 触发
#DE(Interrupt 0x00)。 - IDT 查询
0x00号中断的处理程序地址。 - CPU 执行
exception_handler_divide_error:pusha保存寄存器。push $0(无错误码)。push $0(异常号)。- 调用
do_handler_divide_error处理异常。 iret让 CPU 恢复执行,但如果3 / 0代码执行不下去,可能 进入for(;;)死循环或kernel panic。
5. 可能的调试方法
(1) 确认 IDT 设置是否正确
如果 3/0 发生时 CPU 没有进入 exception_handler_0,检查:
void idt_init() {
set_idt_gate(0, (uint32_t)exception_handler_divide_error, 0x08, 0x8E);
}
(2) 通过 GDB 观察异常触发
在 QEMU + GDB 环境下:
b exception_handler_divide_error
c
如果 b exception_handler_divide_error 断点命中,说明 IDT 正确。
(3) 让 do_handler_divide_error 打印信息
void do_handler_divide_error(uint32_t esp) {
printf("Divide Error Exception!\n");
while (1);
}
如果 未打印消息,可能是 IDT 设置错误或 CLI 禁用了中断。
6. 结论
✅ 为什么 3 / 0 会调用 exception_handler_divide_error?
3 / 0触发 CPU 0x00 号中断(除零异常)。- CPU 查询 IDT,找到
exception_handler_divide_error。 - 执行
pusha,保存寄存器,然后调用do_handler_divide_error。 iret让 CPU 恢复执行,但通常进入死循环或kernel panic。
✅ 调试方向
- 确保
IDT正确指向exception_handler_0。 - 用 GDB 或
printf确认do_handler_divide_error是否执行。 - 检查
cli是否禁用了中断(导致异常未触发)。
🚀 你的 OS 现在已经正确响应 CPU 异常了,下一步可以处理 Page Fault(0x0E)或 General Protection Fault(0x0D)! 🔥

被折叠的 条评论
为什么被折叠?



