GDT(全局描述符表)、LDT(局部描述符表)

📅 发布时间:2026/8/4 12:08:10
GDT(全局描述符表)、LDT(局部描述符表)
它们是 x86 架构内存分段机制的核心数据结构虽然现代 64 位系统淡化了对段基址和长度的使用但它们仍然是 CPU 进行特权级管理、模式切换和任务隔离的基石。一、GDT全局描述符表1. 什么是 GDTGDT 是 CPU 中一张全局唯一的表由操作系统在系统初始化时创建存储在内存中。它包含多个段描述符Segment Descriptor每个描述符定义了一个内存段的基地址、大小、访问权限和类型。全局性整个系统所有 CPU 核心共享一张 GDT每个核心有一个副本但内容相同。位置由 CPU 的GDTR全局描述符表寄存器指向包含 GDT 的线性基地址和大小限制。2. 段描述符的结构64 位每个段描述符占8 字节在 64 位模式下扩展为16 字节但低 8 字节仍与 32 位兼容。以下是其关键字段------------------------------------------------------------------------ | Base (31:24) | Flags (G|D/B|L|AVL) | Limit (19:16) | Access Rights | ------------------------------------------------------------------------ | Base (23:16) | Base (15:0) | Limit (15:0) | | ------------------------------------------------------------------------字段含义Base32/64 位段的线性地址基址。在 64 位模式下CS/SS/DS/ES 的基址强制为 0但 FS/GS 可通过 MSR 设置独立基址。Limit20 位段的大小以字节或页面为单位。在 64 位模式下此字段被忽略地址空间视为全 64 位。GGranularity0Limit 以字节为单位1Limit 以 4KB 页为单位。D/BDefault/Big016 位段132 位段在 64 位模式中通常为 1。LLong Mode1表示该段为 64 位代码段仅 CS 有效。AVLAvailable操作系统可自由使用的位。Access Rights8 位包含段类型代码/数据、可读/可写、特权级DPL等权限信息。3. GDT 中的典型条目一个典型的 Linux 内核 GDTx86_64包含以下条目索引名称用途关键属性0NULL 描述符必须为全 0用于防止未初始化的段选择子导致错误-1KERNEL_CS内核代码段ring 0DPL0L1可执行2KERNEL_DS内核数据段ring 0DPL0可读/写3USER_CS用户代码段ring 3DPL3L1可执行4USER_DS用户数据段ring 3DPL3可读/写5TSS任务状态段用于任务切换和 IST 栈DPL0系统段类型4. GDTR 寄存器GDTR48 位 ------------------------------------ | Limit (16 位) | Base (32/64 位) | ------------------------------------BaseGDT 在线性地址空间中的起始地址。LimitGDT 的大小字节数减 1。加载 GDT 的指令lgdt [gdtr_addr]在保护模式下。二、LDT局部描述符表1. 什么是 LDTLDT 是每个进程/任务私有的描述符表用于隔离不同进程的段。在 x86 架构中LDT 允许每个进程拥有自己独立的段映射从而实现更强的隔离性。局部性每个进程可以有自己的 LDT但现代操作系统通常不使用 LDT。位置由 CPU 的LDTR局部描述符表寄存器指向LDTR 存储的是 LDT 的段选择子指向 GDT 中的 LDT 描述符。2. LDT 的结构LDT 的结构与 GDT 完全相同也是由段描述符组成但 LDT 本身是一个系统段由 GDT 中的一个描述符指向。GDT 中的 LDT 描述符 ------------------------------------ | LDT Base (32位) | LDT Limit (20位) | ------------------------------------ | Type LDT (2) | DPL 0 (内核) | ------------------------------------LDTR 包含两部分可见部分LDT 的段选择子指向 GDT 中的 LDT 描述符。不可见部分LDT 的基地址和大小由 CPU 缓存。3. LDT 在现代操作系统中的使用Linux默认不使用 LDT但通过modify_ldt(2)系统调用允许用户态进程创建自己的 LDT主要用于运行遗留的 16 位代码或某些模拟器。Windows不使用 LDT完全依赖分页和基址寄存器。实际用途主要用于x86 虚拟化如 VMware、VirtualBox 中的段仿真或运行16 位 DOS 程序如 DOSBox。三、GDT vs LDT 对比特性GDTLDT作用域全局整个系统共享局部每个进程可选拥有自己的 LDT寄存器GDTR基址 大小LDTR存储 LDT 段选择子条目数量最多 8192 个64 位模式下最多 8192 个CPU 硬件支持强支持必须存在支持但现代操作系统可禁用在现代 OS 中的使用必须用于内核/用户 CS/DSTSS 等可选用于遗留代码或特定模拟器段选择子中的 TI 位01四、GDT 与 LDT 在 x86_64 长模式下的地位GDT 仍必须存在用于定义CS、DS等段选择子的基本权限特别是 DPL 和 L 位。LDT 几乎被弃用由于 64 位模式不再支持 16 位分段LDT 在 x86_64 上仅用于兼容性目的例如运行 32 位代码或 DOS 仿真。FS/GS 替代 LDT在现代操作系统中线程本地存储TLS和 CPU 核心数据per_cpu通过FS/GS和 MSRIA32_FS_BASE/IA32_GS_BASE实现不再依赖 LDT。五、GDT 访问权限与段选择子的关系段选择子中的RPL请求特权级和 GDT 描述符中的DPL描述符特权级共同决定了访问是否合法条件结果RPL DPL数值越大特权级越低且CPL DPL允许访问否则触发#GP通用保护异常例如用户态代码CPL3试图加载一个DPL0的段选择子到DS会触发#GP。六、操作 GDT/LDT 的相关指令指令作用lgdt [addr]加载 GDT 基址和大小到 GDTRsgdt [addr]存储 GDTR 内容到内存lldt [sel]加载 LDT 选择子到 LDTR由 GDT 中的 LDT 描述符指向sldt [dest]存储 LDTR 内容mov ds, ax将段选择子加载到段寄存器DS/ES/FS/GS/SS七、总结------------------ | 操作系统 | ----------------- | v ----------------- | GDT (全局) | ← 所有 CPU 核心共享 | - NULL 描述符 | | - KERNEL_CS/DS | | - USER_CS/DS | | - TSS 描述符 | | - LDT 描述符 | ----------------- | v ----------------- | LDT (局部) | ← 每个进程可选 | - 私有段描述符 | | - 用于隔离/兼容 | ------------------GDT是系统启动后第一个建立的内存管理结构定义了 CPU 最基本的访问模式内核/用户、代码/数据。LDT是 GDT 的扩展用于更细粒度的隔离但在现代 64 位系统中几乎被淘汰。理解 GDT的关键在于它的CS和DS条目它们决定了 CPU 当前运行在哪个特权级ring 0/3。