从零写 OS 内核-第四篇:进入保护模式(段式内存管理详解)
“保护模式 ≠ 分页! 它的第一道防线,是被现代 OS ‘悄悄绕过’的段机制。”
在前面几篇中,我们借助 GRUB 轻松启动了内核,输出了 “Hello World”,还理解了 Multiboot 协议。 但你是否想过: 为什么 GRUB 要帮我们进入保护模式?它到底在保护什么?
今天,我们就来亲手完成这一历史性转变—— 从实模式(Real Mode)切换到保护模式(Protected Mode) ,并深入理解其核心: 段式内存管理(Segmentation) ,包括 GDT、LDT 与段选择子的工作原理。
🔥 即便现代操作系统几乎“废弃”了段机制, 理解它,仍是掌握 x86 架构的必经之路 。
一、什么是实模式(Real Mode)?
实模式 ,是 x86 CPU 上电后的 默认运行状态 ,源自 1978 年的 Intel 8086 处理器。
✅ 特点:
20 位地址线
→ 最大寻址
1MB 内存
(0x00000 ~ 0xFFFFF)
段:偏移寻址
:物理地址 = 段寄存器 × 16 + 偏移
例如:
CS:IP = 0x1000:0x0020
→ 物理地址 =
0x10000 + 0x20 = 0x10020
无内存保护
:任何程序可读写任意内存(包括 BIOS 和显存)
无特权级
:所有代码运行在“上帝模式”
💡 实模式就像一辆没有方向盘、没有刹车的老式拖拉机——简单,但极其危险。
❓ 为什么现代 CPU 还要从实模式启动?
为了兼容! PC 兼容机必须能运行 1980 年代的老 DOS 程序,所以 CPU 上电后“假装自己是 8086”。
二、什么是保护模式(Protected Mode)?
保护模式 由 Intel 80286 (1982)引入,80386 起成为现代操作系统的基石。
✅ 核心优势:
| 特性 | 说明 |
|---|---|
| 32 位寻址 | 可访问 4GB 内存 (后续通过 PAE/64 位扩展) |
| 内存保护 | 防止程序越界访问(段界限、权限检查) |
| 特权级(Ring 0~3) | 内核(Ring 0) vs 用户程序(Ring 3) |
| 虚拟内存基础 | 为分页(Paging)提供支持 |
🌟 保护模式下的内存访问,不再是“直接物理地址”,而是通过 段描述符表 进行翻译和校验。
三、关键机制:段描述符与段寄存器
在保护模式中,段寄存器(CS、DS、SS 等) 不再直接存地址 ,而是存一个 段选择子(Segment Selector) 。
🔍 段选择子结构(16 位):
Index
:在 GDT/LDT 表中的索引(×8 得到字节偏移)
TI
(Table Indicator):0 = GDT,1 = LDT
RPL
(Requested Privilege Level):请求特权级(0~3)
📦 段描述符(64 位)
由 Index 指向的描述符,包含:
段基地址(32 位)
段界限(Limit)
类型(代码/数据/只读/可执行等)
DPL(Descriptor Privilege Level)
G 位(粒度:0=字节,1=4KB)
✅ CPU 通过 GDT(全局描述符表) 或 LDT(局部描述符表) 查找描述符。
四、GDT vs LDT:谁来管理“段”?
| 对比项 | GDT(Global Descriptor Table) | LDT(Local Descriptor Table) |
|---|---|---|
| 作用范围 | 全局,所有任务共享 | 局部,每个任务可有自己的 LDT |
| 数量 | 系统只有一个 | 可有多个(每个进程一个) |
| 使用频率 | 极高 (内核、用户代码/数据段) | 极低(现代 OS 几乎不用) |
| 切换方式 | LGDT 指令加载 | 通过 GDT 中的一个特殊描述符指向 LDT |
📌 现代操作系统(如 Linux)几乎完全弃用 LDT ,转而依赖 分页机制 实现内存隔离。 但在进入保护模式初期, GDT 是必须的 !
五、实战:如何从实模式进入保护模式?
即使你用 GRUB 启动(它已切换), 手动实现一次,才能真正理解 。 以下是裸机环境下切换的完整步骤:
步骤 1:关闭中断
步骤 2:加载 GDT
先定义 GDT(通常在汇编中):
加载 GDT:
步骤 3:设置 CR0 的 PE 位(Protection Enable)
步骤 4:远跳转,刷新 CS 段寄存器
🔥 这一步至关重要! 因为 jmp 是 远跳转(far jump) ,会同时加载 CS 和 EIP ,从而让 CPU 用新的段描述符解释后续指令。
步骤 5:设置其他段寄存器
步骤 6:进入 32 位代码段(NASM 需用 [bits 32] )
六、为什么 GRUB 要帮我们做这些?
因为:
实模式下无法访问 1MB 以上内存
无法实现内存保护和多任务
现代操作系统必须运行在保护模式(或长模式)下
GRUB 在加载 Multiboot 内核前,已经完成了:
- 加载 GDT
- 设置 CR0.PE = 1
- 远跳转到 32 位代码段
- 设置好段寄存器
所以你的内核代码 天然运行在保护模式 ——但这不等于你可以忽略它!
七、下一步:准备开启分页!
进入保护模式只是第一步。 接下来,你需要:
启用分页(Paging)
→ 实现虚拟内存
设置中断描述符表(IDT)
→ 处理异常和硬件中断
初始化堆和内存管理器
→ 为
kmalloc
打基础
而这一切,都建立在 你已掌握保护模式 的前提之上。
写在最后
从实模式到保护模式, 不仅是 CPU 工作方式的改变, 更是 操作系统从“玩具”走向“系统”的分水岭 。
今天你写的这几行汇编, 正是 Linux、Windows、macOS 内核启动时也曾走过的路。
🌟 理解历史,才能创造未来。
📬 动手试试吧! 即使你不写裸机启动代码,也建议在 QEMU 中模拟一次手动切换过程。 欢迎在评论区分享你的 GDT 定义或遇到的坑!
👇 下一篇你想看: 分页机制详解 ,还是 中断与异常处理(IDT) ?
操作系统 #内核开发 #x86 #实模式 #保护模式 #GDT #LDT #段机制 #从零开始
📢 彩蛋 :关注后回复关键词 “gdt” ,获取:
完整 GDT 汇编模板(含注释)
实模式 → 保护模式切换的 QEMU 调试脚本
- Intel 手册相关章节摘录(中文)
评论