您有责任了解处理器如何以及为何调用中断服务例程,并相应地为 ISR 编写代码。您试图将由除零错误生成的异常视为由硬件中断生成。然而,这不是 Intel x86 处理器处理此类异常的方式。
x86处理器如何处理中断和异常
有几种不同类型的事件将导致处理器调用中断向量表中给出的中断服务例程。这些统称为中断和异常,处理器可以通过三种不同的方式处理中断或异常:fault, as a trap,或作为abort。除法指令会生成除法错误 (#DE) 异常,该异常将作为错误进行处理。硬件和软件中断作为陷阱进行处理,而其他类型的异常则作为这三种方式之一进行处理,具体取决于异常的来源。
Faults
如果异常的性质允许以某种方式纠正,则处理器将异常作为错误来处理。因此,压入堆栈的返回地址指向生成异常的指令,因此故障处理程序知道导致故障的具体指令,并可以在解决问题后恢复执行故障指令。页面错误 (#PF) 异常就是一个很好的例子。它可用于通过让故障处理程序为故障指令尝试访问的地址提供有效的虚拟映射来实现虚拟内存。有了有效的页面映射,指令就可以恢复并执行,而不会生成另一个页面错误。
Traps
中断和某些类型的异常(所有这些都是软件异常)都作为陷阱进行处理。陷阱并不意味着指令执行过程中出现错误。硬件中断发生在指令执行之间,软件中断和某些软件异常有效地模仿了这种行为。陷阱是通过推送正常执行的下一条指令的地址来处理的。这允许陷阱处理程序恢复被中断代码的正常执行。
Aborts
严重且不可恢复的错误将作为中止处理。只有两种异常会生成中止:机器检查 (#MC) 异常和双重故障 (#DF)。机器检查指令是检测到处理器本身硬件故障的结果,无法修复,并且无法可靠地恢复正常执行。当处理中断或异常期间发生异常时,就会发生双故障异常。这使得 CPU 处于不一致的状态,处于调用 ISR 的所有必要步骤中间的某个位置,并且无法恢复。压入堆栈的返回值可能与导致中止的原因有任何关系,也可能没有任何关系。
通常如何处理除法错误异常
通常,大多数操作系统通过将除法错误异常传递给正在执行的进程中的处理程序来处理,或者通过终止进程来处理失败,表明它已经崩溃。例如,大多数 Unix 系统向进程发送 SIGFPE 信号,而 Windows 使用其结构化异常处理机制执行类似的操作。这样进程的编程语言运行时就可以设置自己的处理程序来实现所使用的编程语言所需的任何行为。由于除以零会导致 C 和 C++ 中的未定义行为,因此崩溃是可以接受的行为,因此这些语言通常不会安装除以零处理程序。
请注意,虽然您可以通过“递增 EIP”来处理除法错误异常,但这比您想象的要困难,并且不会产生非常有用的结果。您不能只向 EIP 添加一个或某个其他常量值,您需要跳过整个指令,该指令的长度可能在 2 到 15 个字节之间。有三种指令可能导致此异常:AAM、DIV 和 IDIV,并且可以使用各种前缀和操作数字节对这些指令进行编码。您需要对指令进行解码才能弄清楚它有多长。执行此增量的结果将如同该指令从未执行过一样。错误指令不会计算出有意义的值,并且您将无法知道程序为何无法正确运行。
阅读文档
如果您正在编写自己的操作系统,那么您需要提供英特尔软件开发人员手册,以便您可以经常查阅。特别是,您需要阅读和学习第 3 卷:系统编程指南中的几乎所有内容,不包括虚拟机扩展章节和后面的所有内容。那里详细介绍了您需要了解的有关如何中断和异常的所有内容,以及您需要了解的许多其他内容。