操作系统进入保护模式

300 阅读9分钟

1.实模式

实模式是x86架构CPU在启动后的初始状态,也是16位处理器如8086或8088的运行模式。在实模式下,CPU可以直接访问物理内存的第一个1MB区域。

实模式的寻址方式是通过段地址和偏移地址两部分组合来实现的。一个完整的内存地址(通常为20位)在实模式下被分为两个部分:一个16位的段地址(Segment)和一个16位的偏移地址(Offset)。

要获取实际的物理地址(Physical Address),CPU会将段地址左移4位(相当于乘以16,因为在二进制下,每左移一位就是乘以2),然后将结果与偏移地址相加。

假设我们有一个段地址为0x1000,偏移地址为0x0050,那么实际的物理地址将被计算为:

    Physical Address = (Segment << 4) + Offset
    Physical Address = (0x1000 << 4) + 0x0050
    Physical Address = 0x10000 + 0x0050
    Physical Address = 0x10050
   

这就是实模式的寻址方式,它使得CPU可以访问到1MB的物理内存。注意,尽管实模式的寻址方式允许访问1MB内存,但实际上,由于IBM PC的历史原因,顶部的384KB被用于特殊目的,因此实模式程序通常只能使用640KB的常规内存。

2.保护模式

保护模式是x86架构中一种更高级别的运行模式,它提供了更强大的内存保护和特权级管理机制。在保护模式下,内存寻址和访问的过程相对复杂一些,但同时也提供了更高的安全性和灵活性。

在保护模式下,内存寻址的过程如下:

  1. 设置段选择器:首先,你需要从某个段寄存器(如CS、DS、ES等)中获取一个段选择器。这个段选择器指向全局描述符表(GDT)或者局部描述符表(LDT)中的某个条目。

  2. 寻找段描述符:然后,你通过段选择器指向的索引在描述符表(GDT或LDT)中找到对应的段描述符。这个描述符包含了段的基地址、限制和权限。

  3. 计算物理地址:接着,你将段描述符中的基地址与偏移地址相加,得到的结果就是物理地址。如果这个物理地址超出了段描述符中的限制,就会触发一个段错误(Segmentation Fault)。

  4. 访问控制:最后,当你尝试访问这个物理地址时,还会检查你的访问权限是否符合段描述符中的规定。如果不符合,就会触发一个保护错误(Protection Fault)。 理解段寄存器、段选择器、GDTR、段描述符以及内存寻址的过程。

在x86架构的CPU中,段寄存器(例如DS)存放的是一个段选择器(Segment Selector)。这个段选择器是一个16位的值,可以被划分为三个部分:请求特权级(RPL, Request Privilege Level)、表指示符(TI, Table Indicator)和索引(Index)。

以下是一个段选择器的示例:

      15                                3   2  0
      +---------------------------------+   +-+
      |            索引                  |   |TI|RPL|
      +---------------------------------+   +-+
  • 索引(Index):这是一个13位的值,它是一个指向GDT或LDT的索引。

  • 表指示符(TI, Table Indicator):这是一个1位的值,它决定了索引是指向GDT还是LDT。如果TI=0,那么索引指向GDT,如果TI=1,那么索引指向LDT。

  • 请求特权级(RPL, Request Privilege Level):这是一个2位的值,它代表了发起访问请求的特权级别。在x86架构中,特权级别有四个(0-3),其中0级是最高的特权级别,3级是最低的特权级别。

假设DS寄存器中的值为0x002B。在这个值中,索引为0x02,表指示符TI为1(指向LDT),请求特权级RPL为3级。这个段选择器代表了特权级3的程序访问LDT中的第三个描述符(索引从0开始)。

段选择器的具体格式可能会根据不同的CPU架构和操作系统有所不同。例如,在64位的x86架构(x86_64或AMD64)中,虽然段寄存器仍然存在,但是大部分段寄存器(除了FS和GS)的值都被忽略,而所有的内存访问都默认在0级特权下进行。

在x86架构中,局部描述符表(LDT)的基地址和界限存储在LDTR(局部描述符表寄存器)中。当我们需要根据LDT的索引获取段描述符时,就需要从LDTR寄存器中读取LDT的基地址。

段描述符的结构

当基于上述32位段描述符结构的分配,下面是一个示例的测试数据:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|     Base 31:24     | Limit 19:16 |  AVL | P | DPL | S | Type |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|    Base 23:16      |    Limit 15:0      |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
测试数据示例:
Base 31:24: 0x05    // 段基址的高8位为 0x05
Limit 19:16: 0x0F   // 段大小的高4位为 0x0F
AVL: 0              // 保留位,未使用
P: 1                // 段存在位,表示该段存在
DPL: 0b00           // 请求特权级为 Ring 0,即最高特权级
S: 1                // 描述符类型位,表示该段为代码段或数据段描述符
Type: 0b0011        // 段类型为数据段,可读写权限
Base 23:16: 0xAB    // 段基址的中间8位为 0xAB
Limit 15:0: 0xFFFF  // 段大小的低16位为 0xFFFF

这个过程大致如下:

  1. 读取LDTR:首先,我们从LDTR寄存器中读取LDT的基地址。

  2. 计算地址:然后,我们根据段选择器中的索引计算出段描述符在LDT中的地址。在x86架构中,每个段描述符占用8个字节,所以段描述符的地址等于LDT的基地址加上索引值乘以8。

例如,假设LDTR的值为0x1000,段选择器中的索引值为2,那么段描述符的地址就是0x1000 + 2 * 8 = 0x1010。

需要注意的是,虽然我们可以通过这种方式计算出段描述符的地址,但是这个地址并不能直接在普通的程序中使用。这是因为段描述符的访问是由CPU的内存管理单元(MMU)自动处理的,普通的程序不能直接访问物理地址。

段检查 需要进行以下检查:

  • 段存在性检查(P):检查段描述符的存在位(P)是否为1,以确定段是否存在。
  • 特权级检查(DPL):检查段描述符的请求特权级(DPL)与当前特权级是否匹配,以验证是否有权限访问该段。
  • 段类型检查(S和Type):检查段描述符的S位和Type字段,验证描述符的类型是否正确。如果S位为1,表示这是一个代码段描述符或数据段描述符。
  • 段限制检查(Limit):根据段描述符的大小限制(Limit)来验证访问的偏移是否在允许的范围内。
  • 地址转换:根据段描述符的基址和偏移地址,将逻辑地址转换为物理地址。

地址计算

偏移地址是根据具体的代码或数据访问操作来提供的。下面是一个操作数据的例子,以说明如何使用偏移地址计算物理地址:

假设我们有一个逻辑地址,由段选择子和偏移地址组成,表示要访问数据的位置。

  1. 获取逻辑地址:假设逻辑地址为:段选择子为0x1234,偏移地址为0x5678。

  2. 根据段选择子获取段描述符:通过段选择子的索引,从全局描述符表(GDT)或局部描述符表(LDT)中获取对应的段描述符。

  3. 获取段基址:从段描述符中获取段基址(Base)。假设段基址为0xABCDEF.

  4. 偏移地址与段基址相加:将偏移地址0x5678与段基址0xABCDEF相加,得到线性地址。

    线性地址 = 段基址 + 偏移地址
             = 0xABCDEF + 0x5678
             = 0xABD357
    
  5. 线性地址即为物理地址:由于假设没有启用页机制,线性地址直接就是物理地址。

    物理地址 = 线性地址
            = 0xABD357
    

这样,通过将偏移地址与段基址相加,我们计算出了访问数据时的物理地址。

需要注意的是,此处的示例仅用于说明计算物理地址的一般过程,并未考虑具体的处理器架构和操作系统的细节。实际的地址转换过程可能会有更多的复杂性和细节,包括地址溢出、段限制检查、特权级验证等。具体的实现和处理方式可能会有所不同。

2.段页门

os是面向cpu的段页门实现的。 总结如下。

  1. 段(Segmentation): 是内存访问隔离的工具。每个段代表了一块独立的内存区域,并且每个段可以设定自己的访问权限,以此隔离不同段之间的内存访问。

  2. 页(Paging): 是虚拟内存系统的基本单元。通过分页,可以将内存的物理地址空间与虚拟地址空间分离,实现灵活的内存访问和管理。

  3. 门(Gates): 用于实现权限的提升和降低。例如,当用户态的程序需要调用操作系统内核的服务时,需要通过一个系统调用门,使得程序能够安全地从用户模式切换到内核模式。

这三者合起来,就构成了操作系统的内存管理和保护机制,以支持多任务环境下的程序隔离和内存保护等功能。