什么是 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 会切换回用户态,恢复用户代码段和数据段。

Architecture
0 声望0 粉丝

知其然, 更要知其所以然~