OSDEV求助:执行int 0x80触发General Protection Fault的原因排查
分析与解决int 0x80触发General Protection Fault的问题
问题背景
在i386平台使用gcc、nasm和qemu开发玩具操作系统时,初始化IDT并添加0x80号中断处理程序后,执行int 0x80指令触发了13号General Protection Fault异常,调试显示错误码为0x402,IDT中0x80和13号条目配置看似正常,但无法定位问题根源。
核心问题分析
1. IDTR的Limit值设置错误
你在init_idt中设置idtr.limit = 255,但IDT包含256个8字节的条目,总大小为256 * 8 = 2048字节。IDTR的Limit字段表示IDT最后一个字节相对于基地址的偏移量,正确值应为总字节数减1(即2047或0x7FF)。
当前Limit仅允许CPU访问IDT的前256字节(对应前32个条目),而0x80号中断对应的IDT条目位于0x2000 + 0x80 * 8 = 0x2200,超出了0x2000 + 255 = 0x20FF的有效范围,CPU无法访问该条目,直接触发GPF。
2. 错误码0x402的验证
错误码0x402的二进制为10000000010,其中:
- 位1为1:表示错误来源于IDT条目
- 位10为1:表示发生了段界限违反,完全对应IDT范围不足的问题
解决方法
步骤1:修正IDTR的Limit值
修改init_idt函数中的Limit设置,确保覆盖所有256个IDT条目:
void init_idt() { for (int i = 0; i < 256; i++) { set_intrdesc(idt + i, 0, 0, 0); } set_intrdesc(idt + 13, general_protection_handler13, KERNEL_CS, AR_INTGATE32); struct {uint16_t limit; uint32_t addr;} __attribute__((packed)) idtr; // 用sizeof计算更健壮,避免硬编码错误 idtr.limit = 256 * sizeof(struct idt_entry_t) - 1; idtr.addr = idt; asm volatile ("lidt %0" :: "m" (idtr)); }
步骤2:提前完成IDT条目配置(可选但推荐)
将0x80号中断的条目配置移到init_idt函数中,避免在开中断后动态修改IDT,减少潜在风险:
void init_idt() { for (int i = 0; i < 256; i++) { set_intrdesc(idt + i, 0, 0, 0); } set_intrdesc(idt + 13, general_protection_handler13, KERNEL_CS, AR_INTGATE32); // 提前配置0x80号中断 set_intrdesc(idt + 0x80, interrupt_handler_0x80, KERNEL_CS, AR_INTGATE32); struct {uint16_t limit; uint32_t addr;} __attribute__((packed)) idtr; idtr.limit = 256 * sizeof(struct idt_entry_t) - 1; idtr.addr = idt; asm volatile ("lidt %0" :: "m" (idtr)); }
对应的main函数简化为:
void main() { init_idt(); init_pic(); asm("sti"); asm("int $0x80"); while (1) { asm("hlt"); } }
额外验证
确认GDT中KERNEL_CS(0x8)是有效的特权级0代码段描述符,确保IDT条目引用的段选择子合法。
内容的提问来源于stack exchange,提问作者wqooopw
相关产品推荐
相关产品推荐

