全局描述符表(GDT)执行过程

目录

一、背景知识

二、GDT 的结构

三、GDT 执行过程详解

步骤 1:在实模式下准备 GDT

步骤 2:加载 GDTR 寄存器

步骤 3:启用保护模式

步骤 4:远跳转刷新 CS 段寄存器

步骤 5:加载其他段寄存器(DS, ES, SS 等)

步骤 6:后续内存访问的段检查

四、关键机制说明

1. 段选择子(Segment Selector)格式

2. 描述符缓存(Hidden Register)

3. 特权级检查(CPL, DPL, RPL)

五、总结:GDT 执行流程图

六、注意事项


全局描述符表(Global Descriptor Table,GDT)是 x86 架构保护模式下用于定义内存段(如代码段、数据段、堆栈段等)属性和访问权限的核心数据结构。GDT 的执行过程涉及 CPU 启动时从实模式切换到保护模式、加载 GDT 寄存器(GDTR)、以及后续通过段选择子(Segment Selector)访问段描述符进行内存访问控制。下面将详细描述 GDT 的完整执行过程。


一、背景知识

在 x86 架构中,CPU 有多种运行模式:

  • 实模式(Real Mode):16 位地址空间,无内存保护,段地址直接左移 4 位 + 偏移。
  • 保护模式(Protected Mode):32/64 位地址空间,支持虚拟内存、分页、特权级、段保护等机制。

GDT 仅在保护模式下使用。


二、GDT 的结构

GDT 是一个由 段描述符(Segment Descriptor) 组成的数组。每个段描述符占 8 字节(64 位),描述一个内存段的以下信息:

  • 段基地址(Base Address):段在物理内存中的起始地址。
  • 段界限(Segment Limit):段的大小(以字节或页为单位)。
  • 访问权限(Access Rights):
    • 类型(代码/数据/系统段)
    • 可读/可写/可执行
    • 特权级(DPL:Descriptor Privilege Level,0~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             ; 访问字节:代码段,可读,DPL=0,存在
    db 11001111b             ; 高 4 位界限 + 标志(G=1, D/B=1, L=0, AVL=0)
    db 0x00                  ; 基地址高 8 位
gdt_
    dw 0xFFFF
    dw 0x0000
    db 0x00
    db 10010010b             ; 数据段,可写,DPL=0,存在
    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 使用 GDTR(Global Descriptor Table Register) 来定位 GDT。GDTR 是一个 48 位寄存器,包含:

  • 16 位界限(Limit):GDT 的字节长度 - 1。
  • 32 位基地址(Base):GDT 在物理内存中的起始地址。

使用 LGDT(Load GDT Register) 指令加载:

lgdt [gdt_descriptor]

此时 GDT 已被 CPU 知晓,但尚未生效,因为 CPU 仍在实模式。


步骤 3:启用保护模式

通过设置 CR0 控制寄存器PE(Protection 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_start
  • 0x08 是段选择子(Selector):
    • 二进制:0000 1000
    • RPL(请求特权级) = 00(最低特权)
    • TI(Table Indicator) = 0(表示使用 GDT,而非 LDT)
    • Index = 0001(即 GDT 中第 1 个描述符,即代码段)

CPU 执行此跳转时:

  1. 从 GDTR 获取 GDT 基地址。
  2. 用选择子的 Index(1)乘以 8,得到偏移 0x08
  3. GDT_base + 0x08 读取 8 字节的代码段描述符。
  4. 验证描述符有效性(Present=1,类型=代码段,DPL ≥ CPL 等)。
  5. 将描述符中的基地址、界限、属性加载到 CS 的隐藏部分(不可见的段描述符缓存)。
  6. 跳转到目标偏移地址(protected_mode_start),使用新的段属性执行代码。

此后,所有通过 CS 访问的指令都基于新的代码段描述符。


步骤 5:加载其他段寄存器(DS, ES, SS 等)

类似地,需用 MOV 指令加载其他段寄存器,使其指向 GDT 中的数据段或堆栈段:

mov ax, 0x10        ; 数据段选择子(Index=2)
mov ds, ax
mov es, ax
mov ss, ax

注意:SS(堆栈段)必须是可写的数据段,且通常与 DS 相同。

每次加载段寄存器时,CPU 都会:

  • 解析段选择子(Index, TI, RPL)
  • 从 GDT(或 LDT)中读取对应描述符
  • 验证权限(如 DPL ≥ CPL,RPL ≤ DPL 等)
  • 将描述符内容缓存到段寄存器的隐藏部分

步骤 6:后续内存访问的段检查

在保护模式下,每次内存访问(如 mov eax, [ebx])都隐含使用某个段寄存器(默认 DS,或显式指定如 mov eax, [es:edi])。

CPU 执行时:

  1. 从段寄存器获取缓存的段描述符(基地址、界限、属性)。
  2. 检查偏移是否在段界限内(若超出则触发 #GP 异常)。
  3. 检查访问权限(如代码段不可写,数据段不可执行)。
  4. 若启用分页,则线性地址 = 段基地址 + 偏移,再经分页转换为物理地址。

四、关键机制说明

1. 段选择子(Segment Selector)格式

15 … 3

2

1–0

Index

TI

RPL

  • Index:GDT/LDT 中描述符的索引(乘以 8 得偏移)。
  • TI:0 = GDT,1 = LDT。
  • RPL:请求特权级(0~3),用于权限检查。

2. 描述符缓存(Hidden Register)

为避免每次内存访问都查 GDT,CPU 将描述符内容缓存在段寄存器的“隐藏部分”。只有在加载段寄存器时才访问 GDT。

3. 特权级检查(CPL, DPL, RPL)

  • CPL(Current Privilege Level):当前代码段的 DPL(在 CS 中)。
  • DPL(Descriptor Privilege Level):段描述符中的特权级。
  • RPL(Requested Privilege Level):段选择子中的特权位。

访问规则(以数据段为例):

max(CPL, RPL) ≤ DPL
否则触发 #GP(General Protection Fault)


五、总结:GDT 执行流程图

实模式启动
   ↓
在内存中定义 GDT(含空描述符、代码段、数据段等)
   ↓
使用 LGDT 加载 GDTR(GDT 基址 + 界限)
   ↓
设置 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 保护模式内存管理和特权控制的基石。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lcreek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值