这是我的假设,对吗?如果我说双重故障只能且必须发生在处理器的内核模式(或 x86 的 Ring 0)下,当发生任何异常(同步)时,而不会在其他地方发生?
如果答案是肯定的,那么在与旧处理器兼容的新处理器中,如果我们想以未定义指令异常的原因保留这种兼容性,我们就不能在以内核模式运行的代码中使用已经定义的指令(在较新的 CPU 中),对吗?还有一个问题。如果 CPU 执行以内核模式运行的代码,则必须以页面错误的原因将其显示在内存中,不是吗?
还有我的补充想法。在状态寄存器中实现“内部 INT 启用位”有什么好处吗?该位将在发生中断/异常及其返回时自动设置和清除,如果发生异常,HW 读取该位,如果已设置,则跳转到异常处理程序地址,否则跳转到双重故障处理程序?
如果它依赖于架构/操作系统,我会选择 MIPS 上的 Linux。
对不起我的英语不好。
答案1
我对 x86 有很多经验,但对 MIPS 却一无所知,抱歉 - 但我相信无论如何以下描述都适用于它。
双重失误仅有的发生在一个非常特殊的情况下:如果在试图开始一个异常处理程序,发生了另一个异常,则放弃第一个异常并改为调用 Double Fault 异常处理程序。
以下是一个简单错误的示例。如果代码尝试访问无效内存:
*(int *)0 = 0xdead;
然后 CPU 将检测到 NULL 指针引用并尝试启动内存故障处理程序。这可能发生在用户 (ring 3) 或管理员 (ring 0) 代码中,并且不会发生双重故障 - CPU 将只是尝试启动内存故障处理程序。
想象一下,如果操作系统有一个错误,并且内存错误处理程序本身处于无效内存中。因此,在尝试开始内存错误处理程序发生故障,导致第一个错误无法处理。然后会调用双重错误处理程序。(如果在尝试处理开始双重故障处理程序,x86 CPU 会因三重故障而关闭。PC 硬件会检测到这种情况并重置 CPU。
我多次强调开始因为一旦故障处理程序开始成功后,将不再发生双重故障。第一个故障已开始处理,CPU 现在可以处理可能出现的任何新故障。如果发生新故障,CPU 不会“记住”自己处于故障处理程序中并导致双重故障。
根据您的示例,如果故障处理程序尝试使用未定义的操作码,则只会在该指令上调用未定义操作码故障处理程序。这不是双重故障,而只是故障中的故障。
当然,如果内存故障处理程序启动,并且在处理过程中引发内存故障,则内存故障处理程序将重新启动。这可能会再次引发相同的内存故障,从而重新启动内存故障处理程序 - 每次都会使用越来越多的堆栈,直到堆栈本身最终溢出,这在 x86 上是一个不同的故障处理程序。
答案2
我能给你的最佳答案是,是的,对于大多数现代 CPU 来说,只有在内核模式下运行的代码才能触发双重或三重失误。由于 ProtectedMode 操作抽象了物理寻址,使得其不再可能分支到无效的寄存器地址,因此非内核启动的指令触发硬故障的情况非常罕见(但并非不可能)。
因此,是的,任何为 CPU 汇编的机器代码,没有保护模式如果没有修改的话,至少应该重新组装,以便在较新的 CPU 上工作。
来自维基百科: https://en.wikipedia.org/wiki/Protected_mode#Virtual_8086_mode
虚拟 8086 模式主条目:虚拟 8086 模式
随着 386 的发布,保护模式提供了英特尔手册所称的虚拟 8086 模式。虚拟 8086 模式旨在允许之前为 8086 编写的代码无需修改即可与其他任务同时运行,而不会损害安全性或系统稳定性。[29]
然而,虚拟 8086 模式并不完全向后兼容所有程序。需要段操作、特权指令、直接硬件访问或使用自修改代码的程序将生成必须由操作系统处理的异常。[30] 此外,在虚拟 8086 模式下运行的应用程序在使用涉及输入/输出 (I/O) 的指令时会生成陷阱,这可能会对性能产生负面影响。[31]
由于这些限制,一些原本设计用于 8086 的程序无法在虚拟 8086 模式下运行。因此,系统软件在处理旧版软件时,要么牺牲系统安全性,要么牺牲向后兼容性。Windows NT 的发布就是这种牺牲的一个例子,它放弃了对“行为不当”的 DOS 应用程序的向后兼容性。[32]
我希望这能有所帮助,如果不够的话,其他人可以填补我理解中的任何空白。