3/0 导致 CPU 产生 **0 号中断**,从而调用 IDT(中断描述符表)中的异常处理程序 ,所以设计时需要初始化IDT表,为了方便找到异常处理程序地址

你的 3 / 0 触发了 除零异常(Divide Error, #DE),这会导致 CPU 产生 0 号中断,从而调用 IDT(中断描述符表)中的异常处理程序。


1. 为什么 3 / 0 会调用 exception_handler

在 x86 架构中,除零异常(Divide Error, #DE)CPU 自动触发的异常,编号是 0x00
3 / 0 发生时,CPU 会执行以下步骤:

  1. 检测到除零错误
  2. 触发 0x00 号中断
  3. 查找 IDT(中断描述符表),找到对应的异常处理程序地址
  4. 跳转到 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 号异常,它会:

  1. 跳转到 IDT[0] 指定的 exception_handler_divide_error
  2. 执行异常处理
  3. 如果 iret 之后 CPU 仍然无法继续,则可能进入 kernel panic

4. 代码执行流程总结

  1. int a = 3 / 0; 发生 除零异常
  2. CPU 触发 #DE(Interrupt 0x00)
  3. IDT 查询 0x00 号中断的处理程序地址
  4. 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)! 🔥

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值