Skip to content

🧠 一、项目背景(你可以这样开场)

我实现了一个基于 x86 架构的自研操作系统 Bootloader(Stage2),其职责是:

  • 从 MBR 接管控制权(0x7C00 → 0x8000)
  • 完成基础硬件初始化(A20 / VESA)
  • 构建 GDT
  • 从实模式切换到保护模式
  • 为后续内核加载做准备

该阶段的核心目标是:

👉 安全、可控地完成从实模式到保护模式的过渡


🔥 二、核心流程(一定要按这个顺序讲)

整个流程必须严格遵循:

text
1. 关闭中断(CLI)
2. 初始化段寄存器
3. 构建 GDT
4. 加载 GDT(LGDT)
5. 设置 CR0.PE = 1
6. 执行 far jump(刷新 CS)
7. 进入保护模式初始化段寄存器

⚠️ 三、核心技术点与坑点(重点)


🔴 1️⃣ 16 位 vs 32 位(本质冲突)

❗问题本质:

x86 在 boot 阶段是:

text
实模式(16位)

但操作系统需要:

text
保护模式(32位)

⚠️ 关键问题:

👉 指令宽度 + 调用约定必须一致


❌ 错误行为:

  • .code16gcc 混用导致 32 位指令编码
  • pushw / pushl 混用
  • call / ret 不匹配

💥 后果:

  • 栈错位
  • SP 异常(例如 0x000a)
  • 返回地址错误 → CPU 跑飞

✅ 正确做法:

统一:

asm
.code16
pushw / popw
call / ret(16位)

💡 面试总结:

在 bootloader 中,必须严格统一指令宽度与调用约定,否则会导致栈结构被破坏,从而引发控制流错误。


🔴 2️⃣ 栈破坏问题(你已经踩过)


❌ 表现:

text
SP 从 0xFFFE → 0x000a

🔥 本质:

text
push / call / ret 宽度不一致

💥 结果:

  • 返回地址错
  • 函数调用链断裂
  • CPU 跳到随机地址执行

✅ 解决:

  • 统一 pushw
  • 禁止 pushl(除非进入32位)

🔴 3️⃣ GDT 构建与加载(lgdt)


📌 GDT 是什么?

👉 全局描述符表(Global Descriptor Table)

用于定义:

  • 代码段
  • 数据段
  • 内存权限

📌 GDTR 结构:

text
limit (16bit)
base  (32bit)

❗lgdt 的本质:

text
加载 GDTR

👉 但:

  • ❌ 不校验内容
  • ❌ 不检查合法性

⚠️ 常见坑:

❌ GDT 地址错误

👉 因为:

text
real mode: 物理地址 = DS * 16 + offset

❗必须确保:

asm
movw $0, %ds
lgdt gdt_ptr

💡 面试说法:

lgdt 只是加载 GDT 描述符,并不会验证其内容正确性,错误的 GDT 会在后续段访问时导致异常。


🔴 4️⃣ 进入保护模式(CR0 + far jump)


❗步骤1:设置 PE 位

asm
movl %cr0, %eax
orl $0x1, %eax
movl %eax, %cr0

❗步骤2:必须 far jump

asm
ljmp $0x08, $pm_entry

🔥 为什么必须跳?

👉 因为:

  • CS 没刷新
  • CPU 仍在旧模式执行

💥 不跳会发生:

  • 指令执行异常
  • triple fault
  • 系统重启

💡 面试金句:

设置 CR0.PE 后必须执行 far jump 来刷新 CS,否则 CPU 不会真正进入保护模式。


🔴 5️⃣ .code16 / .code32(汇编模式陷阱)


❗误区:

你之前写:

asm
.code32
lgdt gdt_ptr

🔥 真相:

👉 .code16 / .code32 只是:

👉 汇编器指令,不是 CPU 指令


💥 CPU 仍然在:

  • 实模式

👉 结果:

❗指令编码与 CPU 状态不匹配 → 执行错误


✅ 正确:

  • 在切换前:.code16
  • 进入保护模式后:.code32

🔴 6️⃣ far jump 地址错误(严重坑)


❌ 错误:

asm
pm_entry - 0x8000

❗问题:

👉 这是“物理地址误差”


🔥 本质:

  • bootloader 已经被加载到正确地址
  • 不需要手动减偏移

💥 后果:

  • 跳到错误地址
  • CPU执行垃圾指令

✅ 正确:

asm
ljmp $selector, $offset

🔴 7️⃣ GDB 无法识别寄存器(你遇到的)


❗现象:

text
Invalid register `ip`

🔥 原因:

👉 当前 CPU 状态异常:

  • 跑飞
  • 非法执行流
  • GDB架构不同步

✅ 正常情况:

模式寄存器
实模式IP
保护模式EIP

🔴 8️⃣ CPU 跑飞(最终问题)


💥 本质:

❗控制流损坏(Control Flow Corruption)


触发原因:

  • 栈错
  • 指令错
  • GDT错
  • 跳转错

结果:

  • 执行非法指令
  • triple fault
  • QEMU 重启

🧠 四、调试方法(工程能力体现)


✅ QEMU 调试

bash
qemu-system-x86_64 -s -S -no-reboot -d int

✅ GDB 查看

plaintext
info registers
x/10i $cs:0

✅ 断点

plaintext
b *0x8000
b *pm_entry

035_multi_terminal-40-g5d72b8b · 5d72b8b · 2026-06-26