80386处理器的保护模式是现代操作系统运行的基础架构。与实模式相比,保护模式带来了三大革命性改进:
关键提示:保护模式下的段寄存器不再直接存储段基址,而是保存指向描述符的选择子(Selector),这是理解保护模式寻址的关键转折点。
通过为每个任务分配独立的局部描述符表(LDT),配合任务状态段(TSS)实现上下文切换。当CPU切换任务时:
这种设计确保任务A无法访问任务B的内存空间,即使它们使用相同的线性地址。
四个特权级(Ring 0-3)通过以下硬件机制实现:
当程序尝试访问段时,CPU会检查:
code复制MAX(CPL, RPL) ≤ DPL
否则触发#GP异常。典型场景:
GDT是系统级数据结构,包含:
典型GDT条目结构:
code复制63 54 53 52 51 48 47 46 44 43 40 39 32
| Base 31:24 |G |D |0 |AVL|Limit 19:16|P |DPL |S | Type | Base 23:16 |
31 16 15 0
| Base 15:0 | Limit 15:0 |
其中关键字段:
每个任务有自己的LDT,包含:
通过LLDT指令加载,存储在任务状态段中。
选择子结构:
code复制15 3 2 1 0
| 描述符索引(13位) |TI |RPL|
例如CS=0x0008表示:
以指令MOV EAX, [DS:0x12345678]为例:
当加载段寄存器时,CPU会缓存描述符信息到不可见的64位缓存寄存器。这包含:
后续访问直接使用缓存值,直到再次加载段寄存器。这解释了为何修改GDT后必须重新加载段寄存器。
| 特性 | 实模式 | 保护模式 |
|---|---|---|
| 段基址计算 | 段值×16 | 描述符指定 |
| 最大段长 | 64KB | 4GB |
| 段对齐 | 16字节边界 | 任意字节边界 |
| 特权级 | 无 | 4级(Ring0-3) |
| 地址线 | 20位(1MB) | 32位(4GB) |
段寄存器用法:
指令差异:
异常处理:
assembly复制gdt_start:
dd 0, 0 ; 空描述符
dw 0xFFFF ; 代码段界限(0-15)
dw 0x0000 ; 基址(0-15)
db 0x00 ; 基址(16-23)
db 0x9A ; P=1, DPL=0, S=1, Type=1010(执行/读)
db 0xCF ; G=1, D=1, Limit(16-19)=0xF
db 0x00 ; 基址(24-31)
; 数据段描述符类似...
gdt_end:
gdtr:
dw gdt_end - gdt_start - 1
dd gdt_start
assembly复制lgdt [gdtr]
assembly复制mov eax, cr0
or eax, 0x00000001
mov cr0, eax
assembly复制jmp 0x0008:flush ; 0x08是代码段选择子
flush:
mov ax, 0x0010 ; 加载数据段选择子
mov ds, ax
三重故障(Triple Fault):
段错误(#GP):
分页异常(#PF):
虽然现代64位系统已进入长模式,但保护模式的核心思想仍在延续:
Linux内核实现:
Windows保护机制:
虚拟化扩展:
理解保护模式不仅有助于开发操作系统内核,对调试复杂内存问题、分析安全漏洞都有重要意义。我曾在一个驱动开发项目中,通过分析GDTR和IDTR寄存器值,成功定位了一个因描述符权限配置错误导致的系统蓝屏问题。这种底层调试经验往往能解决高级工具无法诊断的疑难问题。