Protect-Mode

参考了这篇知乎

保护模式

操作系统从开机到开始运行所经历的:

引导 $\rightarrow$ 加载内核进入内存 $\rightarrow$ 跳入保护模式 $\rightarrow$ 开始执行内核

引导扇区(Boot Sector)

引导扇区是整个软盘的第0个扇区, 在引导扇区中有一个重要的数据结构叫BPB(BIOS Parameter Block). 在引导扇区中, 以BPB_开头的域属于BPB, 以BS_开头的域不属于BPB, 只是引导扇区的一部分

引导程序(Boot Loader)

Boot Loader is a small program that places the operating system (OS) of a computer into memory. When a computer is powered-up or restarted, the basic input/output system (BIOS) performs some initial tests, and then transfers control to the Master Boot Record (MBR) where the boot loader resides. Most new computers are shipped with boot loaders for some version of Microsoft Windows or the Mac OS. If a computer is to be used with Linux, a special boot loader must be installed.

实模式(Real Mode)和保护模式(Protected Mode)是x86架构下的 (关于内存管理和系统资源保护的) 两种工作模式,它们主要区别在于内存访问方式和系统资源的保护性。

8086下的寄存器

实模式

实模式是早期IBM PC兼容机使用的工作模式,其最大的特点是只能访问 1MB (因为地址线只有20位) 以下的物理内存空间。在实模式下,内存地址由16位段寄存器 (见上图) 和16位偏移地址组成,可以直接对物理内存进行操作。那么两个16位的值如何组合成一个20 位的地址呢?实模式采用的方式是把基址先向左移4位,然后再与偏移量相加。即:

  • 物理地址 = 基址(段值) << 4 + 偏移量

但是,在实模式下,不同的软件之间无法彼此隔离和保护,因此存在着安全隐患。

实模式下的中断

保护模式

保护模式是Intel 80386处理器后引入的一种新工作模式,其最大的特点是支持虚拟内存、多任务和多用户环境,并且对系统资源进行了更好的保护。在保护模式下,可以通过分页机制实现虚拟内存空间的管理。同时,保护模式还提供了特权级别机制,将系统资源和进程隔离开来,避免了进程之间的冲突和干扰.

保护模式为了实现对虚拟内存的支持, 引入了段描述符 (Segment Descriptor) 与描述符表 (Descriptor Table) 的概念. 描述符表记录了内存空间中每个段的信息, 并且以表格的形式呈现. 其中的每一个表项, 为一个段描述符. 每个描述符占 64 个bit

段描述符的数据结构

描述符表有多种类型, 并不呈并列关系:

  • GDT: Global Descriptor Table: 系统级别的描述符表, 存放一些公用的描述符 (比方说系统段描述符), 和包含各进程 LDT 首地址的描述符.
  • LDT: Local Descriptor Table: 进程级别的描述符表, 存放本进程内使用的描述符.
  • IDT: Interrupt Descriptor Table (中断描述符表): 存储各种中断、异常和陷阱事件的处理程序 (也称为中断服务例程).

在这里我们主要研究 GDT 和 LDT. 为了索引 GDT 和 LDT, 我们引入了两个专用的寄存器:

  • GDTR: 48位寄存器,高 32 位放置GDT首地址, 低16位放置GDT限长 (要想查询系统级别的公用的描述符, 进程需要从 GDTR 寄存器中获得 GDT 的首地址,向它发起查询. )
  • LDTR: 16位寄存器,放置一个特殊的选择子, 用于查找当前进程的 LDT 首地址. (要想查询当前进程的描述符, 进程从 LDTR 寄存器中获得 LDT 的首地址, 向它发起查询.)

GDTR / LDTR 中存储的数据结构叫做选择子 (Selector), 用来指向 DT 中的一个表项.

  • [4, 16) 用于表示一个偏移量, 指向了GDT或LDT中的某个段描述符.

  • [3, 4) TI 为0说明指向GDT, 否则指向LDT

  • [0, 3) Request Privilege Level (请求特权级)

img

LDT 和 GDT 从本质上说是相同的, 但是 LDT 嵌套在GDT 之中. LDTR 记录 LDT 的起始位置. 与 GDTR 不同, LDTR 的内容是一个段选择子. 由于 LDT 本身同样是一段内存,也是一个段, 所以它也有个描述符描述它, 这个描述符就存储在 GDT 中, 对应这个描述符也会有一个选择子, LDTR 装载的就是这样一个选择子. LDTR 可以在程序中通过使用 lldt 指令随时改变.

使用 GDT 索引物理地址

这个图丑是丑点但是还蛮形象的

使用 LDT 索引物理地址

img

具体来说, 保护模式下的寻址过程 (逻辑地址 $\to$ 物理地址) 如下:

  1. 解析逻辑地址:在保护模式下,程序员使用的是逻辑地址,需要将其转换为物理地址。这个过程由硬件中的内存管理单元(Memory Management Unit, MMU)完成。
  2. 分段:分段是指将线性地址(即逻辑地址)划分为不同的段,每个段具有特定的属性和权限,例如代码、数据、堆栈等。这个过程由操作系统中的段描述符表(Segment Descriptor Table)控制。
  3. 段选择器:为了访问某个段,需要使用一个称为段选择器的值,它实际上是一个索引,指向段描述符表中的一个条目。这个过程由CPU中的段寄存器完成。
  4. 计算物理地址:通过组合段基址和偏移量来计算出物理地址。其中段基址是段描述符表中的一个字段,而偏移量则是程序员在代码中使用的相对地址。
  5. 访问内存:最后一步是使用计算出的物理地址来访问内存中的数据。

接下来我们研究IDT:

为了索引 IDT, 我们引入一个专门的寄存器叫做 IDTR: IDT表可以驻留在线性地址空间的任何地方,处理器使用IDTR寄存器来定位IDT表的位置。这个寄存器中含有IDT表32位的段基址和16位的段限长。IDT表基地址应该对齐在8字节边界上以提高处理器的访问效率。限长值是以字节为单位的IDT表的长度.

IDT在保护模式下用来存储各种类型的中断描述符. IDT存储的中断描述符总共有三种: 任务门、中断门和自陷门. 在数据结构上, 每个中断描述符的构成为8个连续的字节. 其组成如下:

主要包含段选择子, 偏移量, 一些类型信息, 一些属性信息.

保护模式下的中断

保护模式下中断程序地址如何得到?

  • 首先,我们先从IDTR 寄存器中获取到中断描述符表对应的段基址和段限长,这就得到了中断描述符表
  • 然后,我们用中断类型码 × 8得到相应的中断描述符的存储起始地址的偏移量,这就得到了中断类型码对应的中断门 (根据定义, 中断门是一种中断描述符)
  • 最后,我们从中断门中得到段选择符和偏移量以段选择符为段选择子,再根据第2位的情况,从GDT或LDT得到相应的段描述符,结合偏移量,得到相应中断处理程序的起始地址