全局描述符表(GDT)执行过程
目录一、背景知识二、GDT 的结构三、GDT 执行过程详解步骤 1在实模式下准备 GDT步骤 2加载 GDTR 寄存器步骤 3启用保护模式步骤 4远跳转刷新 CS 段寄存器步骤 5加载其他段寄存器DS, ES, SS 等步骤 6后续内存访问的段检查四、关键机制说明1. 段选择子Segment Selector格式2. 描述符缓存Hidden Register3. 特权级检查CPL, DPL, RPL五、总结GDT 执行流程图六、注意事项全局描述符表Global Descriptor TableGDT是 x86 架构保护模式下用于定义内存段如代码段、数据段、堆栈段等属性和访问权限的核心数据结构。GDT 的执行过程涉及 CPU 启动时从实模式切换到保护模式、加载 GDT 寄存器GDTR、以及后续通过段选择子Segment Selector访问段描述符进行内存访问控制。下面将详细描述 GDT 的完整执行过程。一、背景知识在 x86 架构中CPU 有多种运行模式实模式Real Mode16 位地址空间无内存保护段地址直接左移 4 位 偏移。保护模式Protected Mode32/64 位地址空间支持虚拟内存、分页、特权级、段保护等机制。GDT 仅在保护模式下使用。二、GDT 的结构GDT 是一个由段描述符Segment Descriptor组成的数组。每个段描述符占8 字节64 位描述一个内存段的以下信息段基地址Base Address段在物理内存中的起始地址。段界限Segment Limit段的大小以字节或页为单位。访问权限Access Rights类型代码/数据/系统段可读/可写/可执行特权级DPLDescriptor Privilege Level0~3存在位Present其他标志如粒度Granularity、默认操作数大小D/B 位等。三、GDT 执行过程详解步骤 1在实模式下准备 GDT在操作系统引导阶段如 bootloader 或内核初始化早期CPU 处于实模式。此时需在内存中预先定义好 GDT 表。示例伪代码/汇编风格gdt_start: dq 0x0000000000000000 ; 空描述符必须存在索引 0 gdt_code: dw 0xFFFF ; 段界限低 16 位 dw 0x0000 ; 基地址低 16 位 db 0x00 ; 基地址中间 8 位 db 10011010b ; 访问字节代码段可读DPL0存在 db 11001111b ; 高 4 位界限 标志G1, D/B1, L0, AVL0 db 0x00 ; 基地址高 8 位 gdt_ dw 0xFFFF dw 0x0000 db 0x00 db 10010010b ; 数据段可写DPL0存在 db 11001111b db 0x00 gdt_end: gdt_descriptor: dw gdt_end - gdt_start - 1 ; GDT 界限字节数 - 1 dd gdt_start ; GDT 基地址32 位注意GDT 的第一个描述符索引 0必须为空null descriptor用于无效段选择子。步骤 2加载 GDTR 寄存器CPU 使用GDTRGlobal Descriptor Table Register来定位 GDT。GDTR 是一个 48 位寄存器包含16 位界限LimitGDT 的字节长度 - 1。32 位基地址BaseGDT 在物理内存中的起始地址。使用LGDTLoad GDT Register指令加载lgdt [gdt_descriptor]此时 GDT 已被 CPU 知晓但尚未生效因为 CPU 仍在实模式。步骤 3启用保护模式通过设置CR0 控制寄存器的PEProtection Enable位第 0 位来开启保护模式mov eax, cr0 or eax, 1 ; 设置 PE 位 mov cr0, eax执行完此指令后CPU 进入保护模式但当前段寄存器CS、DS 等仍包含实模式下的值这些值现在被解释为段选择子Segment Selector。⚠️ 此时必须立即执行远跳转far jump以刷新 CS 寄存器使其指向 GDT 中的有效代码段描述符。步骤 4远跳转刷新 CS 段寄存器执行一个远跳转far jump目标地址包含新的段选择子和偏移jmp 0x08:protected_mode_start0x08是段选择子Selector二进制0000 1000RPL请求特权级 00最低特权TITable Indicator 0表示使用 GDT而非 LDTIndex 0001即 GDT 中第 1 个描述符即代码段CPU 执行此跳转时从 GDTR 获取 GDT 基地址。用选择子的 Index1乘以 8得到偏移0x08。从GDT_base 0x08读取 8 字节的代码段描述符。验证描述符有效性Present1类型代码段DPL ≥ CPL 等。将描述符中的基地址、界限、属性加载到 CS 的隐藏部分不可见的段描述符缓存。跳转到目标偏移地址protected_mode_start使用新的段属性执行代码。此后所有通过 CS 访问的指令都基于新的代码段描述符。步骤 5加载其他段寄存器DS, ES, SS 等类似地需用 MOV 指令加载其他段寄存器使其指向 GDT 中的数据段或堆栈段mov ax, 0x10 ; 数据段选择子Index2 mov ds, ax mov es, ax mov ss, ax注意SS堆栈段必须是可写的数据段且通常与 DS 相同。每次加载段寄存器时CPU 都会解析段选择子Index, TI, RPL从 GDT或 LDT中读取对应描述符验证权限如 DPL ≥ CPLRPL ≤ DPL 等将描述符内容缓存到段寄存器的隐藏部分步骤 6后续内存访问的段检查在保护模式下每次内存访问如mov eax, [ebx]都隐含使用某个段寄存器默认 DS或显式指定如mov eax, [es:edi]。CPU 执行时从段寄存器获取缓存的段描述符基地址、界限、属性。检查偏移是否在段界限内若超出则触发 #GP 异常。检查访问权限如代码段不可写数据段不可执行。若启用分页则线性地址 段基地址 偏移再经分页转换为物理地址。四、关键机制说明1. 段选择子Segment Selector格式15 … 321–0IndexTIRPLIndexGDT/LDT 中描述符的索引乘以 8 得偏移。TI0 GDT1 LDT。RPL请求特权级0~3用于权限检查。2. 描述符缓存Hidden Register为避免每次内存访问都查 GDTCPU 将描述符内容缓存在段寄存器的“隐藏部分”。只有在加载段寄存器时才访问 GDT。3. 特权级检查CPL, DPL, RPLCPLCurrent Privilege Level当前代码段的 DPL在 CS 中。DPLDescriptor Privilege Level段描述符中的特权级。RPLRequested Privilege Level段选择子中的特权位。访问规则以数据段为例max(CPL, RPL) ≤ DPL否则触发 #GPGeneral Protection Fault五、总结GDT 执行流程图实模式启动 ↓ 在内存中定义 GDT含空描述符、代码段、数据段等 ↓ 使用 LGDT 加载 GDTRGDT 基址 界限 ↓ 设置 CR0.PE 1进入保护模式 ↓ 执行 far jump 到新代码段刷新 CS ↓ 加载 DS/ES/SS 等段寄存器指向 GDT 中数据段 ↓ CPU 使用段描述符进行内存访问控制基址界限权限 ↓ 正常运行保护模式下的操作系统六、注意事项在 64 位长模式Long Mode下段机制被大幅简化CS/DS/ES/SS 的基地址强制为 0界限忽略除 FS/GS 外GDT 仍需存在用于定义 FS/GS 基址及 TSS。GDT 本身必须位于物理内存中且在保护模式启用后不能随意移动除非更新 GDTR 并刷新所有段寄存器。操作系统通常在内核初始化早期设置 GDT并可能在后续切换到更复杂的 GDT如支持多核、TSS 等。通过以上过程GDT 成为 x86 保护模式内存管理和特权控制的基石。