理论讲解:保护模式
1. 为什么需要保护模式
实模式(Real Mode)有严重的限制:
| 实模式 | 保护模式 |
|---|---|
| 16位操作 | 32位操作 |
| 只能访问1MB内存 | 可访问4GB内存 |
| 没有内存保护 | 有内存保护机制 |
| 没有特权级 | 4个特权级(Ring 0-3) |
| 程序可以随意覆盖任何内存 | 错误的内存访问会触发异常 |
保护模式是所有现代操作系统的基础。
2. GDT(全局描述符表)
什么是GDT
GDT是保护模式下定义内存段的数据结构。每个表项(段描述符)8字节,描述一个内存段的属性。
为什么需要GDT
在保护模式下,段寄存器(CS、DS等)不再直接存放段基址,而是存放一个选择子(Selector),指向GDT中的某个表项。
实模式: CS = 0x07C0
物理地址 = 0x07C0 × 16 + 偏移
保护模式: CS = 0x08 (选择子,指向GDT第1项)
物理地址 = GDT[1].base + 偏移
段描述符结构(8字节)
字节 位 内容
──────────────────────────────────
0-1 0-15 段界限(低16位)
2-3 16-31 段基址(低16位)
4 32-39 段基址(中8位)
5 40-47 访问字节
6 48-55 标志 + 段界限(高4位)
7 56-63 段基址(高8位)
访问字节(第5字节)详解
7 6 5 4 3 2 1 0
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ P │ DPL │ S │ Type │
└───┴───┴───┴───┴───┴───┴───┴───┘
P = Present(1=段有效)
DPL = Descriptor Privilege Level(0=内核, 3=用户)
S = 描述符类型(1=代码/数据段, 0=系统段)
Type = 具体类型
代码段: 1010 = Execute/Read
数据段: 0010 = Read/Write
最小GDT配置
我们需要至少3个描述符:
- 空描述符(第0项,必须为空)
- 代码段描述符(覆盖全部4GB,可执行和读取)
- 数据段描述符(覆盖全部4GB,可读写)
这种配置叫做平坦内存模型(Flat Memory Model)——段基址=0,段界限=4GB,相当于没有分段。
3. 切换到保护模式的步骤
① 关闭中断 (CLI)
- 保护模式的中断处理方式不同
- 切换期间不能响应中断
↓
② 加载GDT (LGDT)
- 告诉CPU GDT在哪里
↓
③ 设置CR0的PE位
- CR0寄存器的第0位 = Protection Enable
- 设为1进入保护模式
↓
④ 远跳转 (FAR JMP)
- 刷新CPU流水线
- 同时设置CS为代码段选择子
↓
⑤ 初始化段寄存器
- DS, SS, ES, FS, GS 设为数据段选择子
↓
⑥ 设置栈指针
- 32位模式下重新设置ESP
↓
⑦ 开始执行32位代码
4. 进入保护模式后的变化
不能再使用BIOS中断
BIOS中断是为实模式设计的。进入保护模式后:
- ❌
int 0x10打印字符 - ❌
int 0x13读取磁盘 - ✅ 直接操作VGA显存(0xB8000)
- ✅ 直接操作I/O端口
VGA文本模式显存
地址 0xB8000: 屏幕第(0,0)位置
[字符] [属性] [字符] [属性] ...
属性字节:
高4位: 背景颜色
低4位: 前景颜色
0x0F = 白色前景,黑色背景
0x0A = 绿色前景,黑色背景
参考资料
- OSDev Wiki - GDT
- OSDev Wiki - Protected Mode
- Intel Manual Volume 3, Chapter 3: Protected-Mode Memory Management