从零写 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 内核前,已经完成了:

  1. 加载 GDT
  2. 设置 CR0.PE = 1
  3. 远跳转到 32 位代码段
  4. 设置好段寄存器

所以你的内核代码 天然运行在保护模式 ——但这不等于你可以忽略它!


七、下一步:准备开启分页!

进入保护模式只是第一步。 接下来,你需要:

启用分页(Paging)

→ 实现虚拟内存
设置中断描述符表(IDT)

→ 处理异常和硬件中断
初始化堆和内存管理器

→ 为
kmalloc
打基础

而这一切,都建立在 你已掌握保护模式 的前提之上。


写在最后

从实模式到保护模式, 不仅是 CPU 工作方式的改变, 更是 操作系统从“玩具”走向“系统”的分水岭 。

今天你写的这几行汇编, 正是 Linux、Windows、macOS 内核启动时也曾走过的路。

🌟 理解历史,才能创造未来。


📬 动手试试吧! 即使你不写裸机启动代码,也建议在 QEMU 中模拟一次手动切换过程。 欢迎在评论区分享你的 GDT 定义或遇到的坑!

👇 下一篇你想看: 分页机制详解 ,还是 中断与异常处理(IDT) ?


操作系统 #内核开发 #x86 #实模式 #保护模式 #GDT #LDT #段机制 #从零开始


📢 彩蛋 :关注后回复关键词 “gdt” ,获取:
完整 GDT 汇编模板(含注释)
实模式 → 保护模式切换的 QEMU 调试脚本

  • Intel 手册相关章节摘录(中文)
最后修改: 2026年7月1日

作者

评论

发表评论

您的邮箱地址不会被公开。