什么是 GDT?
GDT 是一个包含段描述符(Segment Descriptor)的数组,每个描述符定义了一个内存段的基地址、大小和权限。GDT 的作用是帮助 CPU 在保护模式下进行内存分段管理。
GDT(Global Descriptor Table,全局描述符表)与内存分为用户态和内核态之间有密切的关系,特别是在 x86 架构的计算机系统中。GDT 用于定义内存段的特性,这些段可以描述代码段、数据段、堆栈段等,并且可以为这些段设置不同的权限和特权级别。
段描述符
每个段描述符通常是 8 字节(64 位)的结构,包含以下信息:
- 基地址(Base Address):段的起始地址。
- 段限长(Limit):段的大小。
- 权限(Access Rights):段的访问权限和类型信息。
- 标志(Flags):其他控制标志,如粒度(Granularity)和是否为 64 位代码段等。
常见的段类型
GDT 中的段描述符可以用来定义各种类型的段,包括:
- 代码段:包含可执行代码。
- 数据段:包含可读写数据。
- 系统段:用于特殊用途,如任务状态段(Task State Segment, TSS)。
GDT 在 Linux 中的使用
在 Linux 内核初始化时,会设置一个 GDT 并加载到 CPU 中。虽然现代 Linux 系统主要依赖分页机制进行内存管理,但 GDT 仍用于以下目的:
- 切换到保护模式:在启动过程中,Linux 内核会将 CPU 从实模式切换到保护模式,这是操作系统运行的基本要求。保护模式需要使用 GDT。
- 定义内核和用户空间段:GDT 定义了内核态和用户态的代码段和数据段。这些描述符用于设置 CPU 的段寄存器(如 cs、ds、es、fs、gs 和 ss),从而实现特权级别控制。
- 任务状态段(TSS):用于硬件任务切换和处理特权级别变化(如从用户态切换到内核态时)。
GDT 与用户态和内核态的关系
内存段和特权级别
GDT 中的每个段描述符可以定义一个段的基地址、大小以及访问权限等信息。每个段还可以设置特权级别(Privilege Level),通常有四个特权级别(0 到 3),其中:
- Ring 0: 内核态(最高权限)。
- Ring 3: 用户态(最低权限)。
操作系统通过使用 GDT 来管理这些段,并且设置合适的特权级别,以确保用户态代码不能直接访问内核态代码和数据。这种机制被称为分段保护(Segmentation Protection)。
代码段和数据段
通常,GDT 会包含多个段描述符,包括但不限于:
- 内核代码段(Kernel Code Segment):运行在 Ring 0,只有内核态代码能执行。
- 内核数据段(Kernel Data Segment):运行在 Ring 0,只有内核态代码能访问。
- 用户代码段(User Code Segment):运行在 Ring 3,用户态程序执行的代码段。
- 用户数据段(User Data Segment):运行在 Ring 3,用户态程序访问的数据段。
GDT 的作用
内存保护:
- 通过 GDT 中的段描述符和特权级别,操作系统可以确保用户态程序(Ring 3)不能访问或执行内核态(Ring 0)的代码和数据。这是实现用户态与内核态隔离的基础。
段选择器:
- 用户态和内核态的程序通过段选择器(Segment Selector)来访问 GDT 中的段描述符。段选择器位于段寄存器(如 CS、DS、SS 等)中。
- 特权级别通过段选择器的 RPL(Requested Privilege Level)字段和段描述符中的 DPL(Descriptor Privilege Level)字段共同决定。
上下文切换和系统调用:
- 当用户态程序通过系统调用进入内核态时,CPU 会切换到相应的内核代码段和数据段。这通常是通过软件中断(如 int 0x80)或 syscall 指令实现的。
- 系统调用处理完毕后,CPU 会切换回用户态,恢复用户代码段和数据段。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。