x86保护模式下执行sti指令后计算机重启问题排查
保护模式下
sti触发重启的常见原因排查 这种问题我在折腾自己的x86 OS内核时踩过好几个坑,保护模式下加载IDT后执行sti就重启,几乎都是IDT配置或者中断处理环节出了问题,下面是几个最值得优先排查的方向:
IDT描述符配置错误
这是最常见的原因。每个中断门/陷阱门描述符的字段都不能错:- 段选择子必须指向一个有效的32位代码段,权限位(RPL)要和当前特权级匹配;
- 中断处理函数的偏移地址要分高低16位正确填入,别搞反了顺序;
- 描述符的类型字段要正确:32位中断门是
0xE,陷阱门是0xF,如果写成其他值,CPU无法识别会直接触发故障; - 属性位中的P(Present)位必须设为1,否则CPU会认为这个门无效,触发#NP异常。
lidt指令的参数错误
加载IDT时,lidt指向的是一个6字节的结构:前2字节是IDT的限长(总字节数-1),后4字节是IDT的线性基地址。- 限长计算错误:比如你有256个8字节的描述符,总长度是2048,限长应该是
2047,如果写成2048,CPU访问IDT末尾时会越界; - 基地址错误:保护模式下
lidt用的是线性地址,如果开启了分页,要确保这个地址已经被正确映射到物理内存,而且地址本身要对齐(虽然不是强制要求,但不对齐可能引发隐含问题)。
- 限长计算错误:比如你有256个8字节的描述符,总长度是2048,限长应该是
挂起的中断未处理
在加载IDT之前如果没执行cli关闭中断,可能已经有硬件中断(比如时钟、键盘)处于挂起状态。当你执行sti打开中断后,CPU会立刻响应这个中断,但如果对应的IDT门没有正确配置,或者处理函数存在问题,就会触发故障,甚至连锁引发三重故障导致重启。建议加载IDT的流程改成:cli→ 加载IDT → 初始化中断控制器(比如PIC/IOAPIC)屏蔽不必要的中断 →sti。中断处理函数存在问题
就算IDT门配置对了,处理函数的错误也会导致重启:- 没有正确保存和恢复寄存器:中断处理函数开头必须用
pusha(或手动push所有通用寄存器),结尾用popa+iret返回,否则会破坏内核栈和寄存器状态; - 处理函数本身有错误:比如跳转到无效地址、执行非法指令,或者栈溢出;
- 没有正确处理EOI(中断结束信号):对于PIC来说,中断处理完成后必须向PIC发送EOI信号,否则PIC会一直认为中断未处理,后续中断会持续触发,导致系统崩溃。
- 没有正确保存和恢复寄存器:中断处理函数开头必须用
权限不匹配引发的异常连锁
中断门的DPL(描述符特权级)设置要符合规则:- 内核态使用的中断门DPL必须设为0,如果设成更高的特权级,用户态程序可能非法触发内核中断,CPU会触发#GP异常;
- 如果某个异常(比如#GP)对应的IDT门没有配置,CPU会触发双重故障,双重故障的门再没配置就会引发三重故障——这是CPU直接重启的触发条件。所以一定要确保0-31号的异常向量都有对应的处理门,哪怕是一个空的处理函数(至少要能正确
iret返回)。
三重故障的连锁反应
上面提到的任何一种错误,如果引发的异常没有对应的处理门,都会层层升级到三重故障,CPU直接重启。这时候可以用调试工具(比如QEMU+GDB)断点在sti之前,检查IDT的每个描述符内容,或者单步执行看触发了哪个异常。
内容的提问来源于stack exchange,提问作者WhiteFlowers
相关产品推荐
相关产品推荐

