操作系统的设计与实现(二)处理器体系结构

《一个64位操作系统的设计与实现-田宇》学习笔记(二)第六章 处理器体系结构

主要包括处理器的运行模式、控制寄存器器的作用、地址空间、实模式、保护模式、IA32e模式…

实模式中的段管理机制、中断/异常处理机制、分页管理机制

其它参考资料:

Linux内存寻址机制

汇编语言第14章 端口的读写

段寄存器和段描述符

CSDN-任务状态段TSS

百度百科-状态标志寄存器

第六章 处理器体系结构

6.1 基础功能与新特性

6.1.1 运行模式

目前, Intel处理器体系结构大体上可分为IA-32体系结构与IA-32e体系结构两种。32位处理器采用IA-32体系结构, 64位处理器采用IA-32e体系结构。在这两种体系结构中包含着多种运行模式,较为常用的运行模式有IA-32体系结构的保护模式和IA-32e体系结构的64位模式,其他运行模主要用于模式切换或为了程序的兼容性。

  • 实模式

    为处理器提供Intel8086处理器的运行环境,并追加了保护模式和系统管理模式的切换扩展

  • 保护模式

    是32位处理器的主要运行模式,为软件的运行提供了丰富的功能、严格的安全性检测以及向后兼容性

  • 系统管理模式

  • 虚拟8086模式

  • IA-32e模式

    64位处理器的主要运行模式,包含两种模式:兼容模式和64位模式。64位模式为处理器提供64位的线性地址空间并支持超过64G的物理内存寻址,而兼容模式可以使大部分保护模式的程序无需修改就可以运行在64位处理器上

通过上图可以看到,处理器上电或重启首先处于实模式,然后通过CR0控制寄存器的PE标志位可以使处理器运行于保护模式。然后在开启分页机制(CR0控制寄存器的PG标志位)的保护模式下,置位IA32_EFER寄存器的LME标志位可使处理器进入IA-32e模式。

6.1.5 控制寄存器

目前,Intel处理器共有6个控制寄存器(CR0、CR1、CR2、CR3、CR4、CR8)

通过这些寄存器上的标志位,可以控制处理器的运行模式、开启扩展特性以及记录异常状态等功能:

通过mov指令可以对CRn寄存器进行操作,但是保留位必须置0,否则会触发#GP异常

如果在CR0.PE=0时,CR0.PG=1,则会触发#GP异常,说明分页机制必须在保护模式的情况下才能开启。

除了CRn控制寄存器外,EFER寄存器也用于控制系统功能,它是MSR寄存器组的IA32_EFER寄存器,它提供了控制IA-32e运行模式开启的标志位,以及关于页表访问限制的控制区域

6.2 地址空间

地址空间一般就分为虚拟地址空间和物理地址空间,这些地址空间是可以相互转换的,当然,cpu最终需要得到的是物理地址。将当前地址空间(虚拟地址)转换成物理地址空间需要经过若干层转换。例如在借助DMA控制器读取数据时,由于DMA控制器只能访问物理地址,那么程序必须清楚DMA控制器访问的物理内存地址位于当前地址空间的哪个位置(虚拟地址),才能取回DMA读取的数据。

6.2.1 虚拟地址

虚拟地址是抽象地址的统称。(其实在一些资料中Linux下的虚拟地址也可以说等同于线性地址)主要包括逻辑地址和线性地址。我个人的理解:逻辑地址通过分段机制转换成线性地址,线性地址通过分页机制可以转换成物理地址

  • 逻辑地址

    逻辑地址的书写格式为:Segment:Offset (段基地址: 段内偏移)。例如跳转指令指令jmp LoaderBase:LoaderOffset中用的就是逻辑地址,此处的段内偏移地址也可以叫做有效地址(Effective Address),像C语言中获取变量/函数的地址其实得到的就是这个有效地址。逻辑地址最终会转换成为线性地址,但不同运行模式下的转换过程各不相同。

  • 线性地址

    线性地址被划分为几部分,包括页目录项、页表项和页内偏移 (Linux的情况下会分为页上级目录、页中级目录、页夏季目录、页表和页内偏移,以适应64位的情况)。因此,线性地址需要经过分页单元的处理最终转换成为物理地址。

    刚才提到,逻辑地址通过分段单元处理最后转换成线性地址,而在Linux,将各个段的基地址都设为0,因此从数值上逻辑地址是等于线性地址的(本书中,这样的逻辑地址也成为平坦地址Flat Address)。

6.2.2 物理地址

与虚拟地址相对的则是物理地址。物理地址是真实存在与硬件设备上的,它通过处理器的引脚直接或间接地与外部设备、RAM、ROM相连接,即对应于cpu发送到地址总线上的电信号。物理地址空间中不仅包含物理内存(RAM、ROM) 还包括其它硬件设备。在处理器开启分页机制(CR0.PG=1)的情况下,线性地址要通过页表映射才能转换成物理地址;否则线性地址将直接映射为物理地址。

CPU 可以直接读写 3 个地方的数据:CPU 内部的寄存器、I/O端口、内存单元

  • I/O地址

    I/O地址空间与内存地址空间相互隔离,它必须借助特殊的IN/OUT指令去访问。例如,in al,60h指令的执行过程:CPU经过地址线将地址信息60h发出,CPU通过控制线发出端口读命令,选中端口所在的芯片并通知其从中读取数据,端口所在的芯片将60h端口中的数据通过数据线送入CPU,存放在al寄存器中。

    I/O地址空间由65536个可独立寻址的I/O端口组成,即寻址范围0~FFFFh,其中端口地址F8h~FFh保留使用。

  • 内存地址

    内存地址空间不只包含物理内存(RAM、ROM),还包含其它外部硬件设备的地址空间,这些设备与物理内存共享内存地址空间。

    随着时间推移发展,内存地址空间在保持向前兼容的同时又不断增强寻址能力,从而造成物理内存片段化和不连续化,可用物理内存空间、设备地址空间、内存地址空洞等会穿插排列在内存地址空间里。BIOS中断服务程序INT 15h的主功能编号AX=E820h可以获取内存地址空间的相关信息。

6.3 实模式

6.3.1 实模式概述

实模式,Real Mode或Real-Address Mode,是Intel处理器家族的第一种处理模式,现在的话仅用于引导启动程序和更新硬件的ROM。实模式采用独特的段寻址方式,直接访问物理内存。而且在实模式下,通用寄存器的位宽只有16位(这个我觉得也是由于向前兼容性,保持了最初设计使用的位宽长度)。

6.3.2 实模式的段寻址方式

实模式采用逻辑地址编址,即Segment: Offset,其中段基地址保存在段寄存器中,段内偏移地址可以保存在通用寄存器中或使用立即数。实模式的逻辑地址可以通过公式Liner Address = Segment<<4 + Offset转换成线性地址,由于实模式没有分页机制,因此转换出来的线性地址其实也就是物理地址。

实模式的通用寄存器位宽只有16位,因此段的长度最大为64KB。根据公式也可以看出,转换出来的线性地址最长为20位,即通常情况下实模式只能寻址1MB的物理地址空间。通过特殊手段可以将实模式的寻址能力拓展到4GB,实现方法参见第七章。

6.3.3 实模式的中断向量表

实模式中,使用中断向量表(Interrupt Vector Table)IVT来将中断/异常向量号与中断处理程序相关联。IVT中的表项为中断处理程序的起始逻辑地址,一个表项4位,其中2位是Segment,2位是Offset。IVT一共有256个表项,即占用1KB的内存空间(400h)。

通常情况下,实模式的IVT保存于物理地址0处。

6.4 保护模式

保护模式的“保护”是相对实模式而言的。在实模式中,只定义了段寻址方式,即逻辑地址向线性地址转换的规则,却没有限制对目标段的访问权限。这使得应用程序可以随意访问任意的段,包括系统核心代码,毫无安全性可言。而在保护模式下,对目标段进行访问需要足够的权限才可以,这就是“保护”的意义。

6.4.1 保护模式概述

相比于实模式仅基于段的寻址方式。保护模式采用了全新的分段管理机制和分页管理机制,其中分页机制将线性地址空间和物理内存空间进行分业化,使处理器对内存片段的管理和组织更加灵活,效率更高。保护模式下处理器必须先经过分段管理机制将虚拟地址转换成线性地址,再经过分页机制将线性地址转换成物理地址。分页管理机制是可选的,而分段管理机制是必选的。

保护模式同样采用逻辑寻址方式,只不过此时的段寄存器保存的不是段基地址,而是一个索引值(段选择子 Selector)。处理器会根据段选择子从段描述符表中索引出对应的段描述符并将其加载到段寄存器中,段寄存器再从刚才加载的段描述符中取得段的基地址。

处理器为保护模式定义了4个特权级,由高到低分别为0、1、2、3,在大多数操作系统中只使用0和3两个特权级,即常说的内核态(Ring0)和用户态(Ring3)

上面的0、1、2、3是特权级别,而特权级的类型有三种:

  • CPL(Current Privilege Level 当前特权级)。CPL代表当前程序的执行特权级,保存在CS或SS段寄存器的第2个位中。例如当CPL=0,表示系统现在处于“内核态”,当CPL=3,则表示现在处于“用户态”。CPL存放在CS和SS的第0、1位中。

  • DPL(Descriptor Privilege Level 描述符特权级)。DPL用于表示段描述符或者门描述符的特权级,保存在段描述符或者门描述符的DPL区域。当处理器访问段描述符或者门描述符的时候,处理器会对比描述符中的DPL段寄存器中的CPL、以及段选择子中的RPL值

  • RPL(Requested Privilege Level 请求特权级)。RPL和CPL均用于检测目标段的访问权限。也就是说,即使程序拥有足够权限去访问目标段,但如果RPL权限不足,程序依然无法访问目标段。因此,当段选择子的RPL值大CPL (数值越大特权级越低)时,RPL将会覆盖CPL ,反之亦然。

6.4.2 保护模式的段管理机制

保护模式下,逻辑地址需要通过段管理机制转换成线性地址

保护模式下,无论是程序(代码)还是数据,都必须使用段描述符加以修饰。而CPU需要通过段寄存器中的段选择子,从描述符表中索引得到对应的段描述符

段选择子是一个16位的段描述符索引值:

保护模式下的CS、SS、DS、ES、FS、GS段寄存器中除了16位的段选择子以外,还有一段缓存区域。段选择子(16位)是可见的,而缓存区域(80位)是隐藏不可见的不能被任何指令所更改。缓存区域(不可见区域)存放着段描述符的基地址、限长和属性信息。

如下图,当段选择子被处理器加载到段寄存器的可视部分的时候,处理器也会自动将对应的段描述符(包括基地址、限长和属性信息)加载到段寄存器的缓存区域(隐藏部分)。处理器根据缓存区域中的段描述符信息就可以直接进行地址转换了,从而避免了重复读取段描述符的情况,提高了性能。

下图描绘了段选择子索引段描述符的过程,其中的GDTR指向全局描述符表GDT,而LDTR用于从GDT中索引出局部描述符表LDT的段描述符。GDTR和LDTR寄存器中的内容都是伪描述符,具体实例可以看后面的代码示例

  • 全局描述符表(Global Descriptor Table, GDT)。本身并不是一个段,而是一个线性地址空间中的数据结构。使用GDT前,必须使用LGDT汇编指令将其的线性地址和长度加载到GDTR寄存器中。
  • 局部描述符表(Local Descriptor Table)。是一个LDT类型的系统数据段,因此在GDT中必须有一个段描述符来管理这个数据段,如果有多个LDT的话,每个LDT都要在GDT中有一个对应的段描述符。处理器使用LLDR汇编指令可将GDT表内的LDT段描述符加载到LDTR寄存器中。和段寄存器类似,LDTR寄存器同样会保存LDT段描述符的段选择子(可视部分)、线性基地址和长度(隐藏部分)。

若想让操作系统运行在保护模式下,我们必须为操作系统创建至少一个全局描述符表,并将操作系统运行所必须的程序和数据保存在表中,而局部描述符表则可以创建一个、多个、或者不创建。

段描述符的位功能如下:

由上图可知,段描述符是8字节长的,内容包括了段基地址、长度和一些修饰属性,具体功能如下:

其中Type标志位和S标志位结合使用,以指明描述符的类型(代码段或数据段)以及功能(访问权限等)。

示例代码6-1

举个栗子,如下代码是本书bootloader中的一段代码,其中定义了一个全局描述符表,GDT中有一个代码段描述符和一个数据段描述符,还定义了两个段选择子。还定义了一个48位的伪描述符GdtPtr(GDT的长度和线性地址),使用代码lgdt [GdtPtr]即可将伪描述符GdtPtr加载到GDTR寄存器。

代码段描述符

当S标志位置位(1),Type标志位的最高位置位(1)时,表示这个段描述符描述的段是一个代码段。Type标志位的第2位则进一步描述这个段的功能:

表中涉及的C(一致性)、R(可读)、以及A(已访问)三个标志位的具体说明如下

  • A标志位(Accessed,已访问)。记录此代码段是否已被访问过。处理器只负责置位此标志位,并不负责复位。
  • R标志位(Readable,可读)。可执行程序虽然可以被处理器运行,但如果想读取程序段(代码段?)中的数据就必须置位此标志位。
  • C标志位(Conforming,一致性)。代码段可分为一致性代码段和非一致性代码段,通过C标志位进行标识。一个低特权级的程序(代码段)可执行或跳转至一个高特权级(或相同特权级)的一致性代码段,并在执行高特权级代码段的过程中保持低特权级的CPL不变。如果程序想要跳转至一个不同特权级的非一致性代码段,除非使用调用门或者任务门,否则将触发#GP异常。所有的数据段都是非一致性的,意味着它们不能被低特权级的程序访问。

举个栗子,还是这段代码:

示例代码6-1中的代码段描述符LABEL_DESC_CODE32的值为0x00CF9A000000FFFF,可以将它根据描述符位功能表拆解:

从这个代码段描述符的拆解表可以看出来,这个代码段覆盖了整个32位线性地址空间(0~4GB),而且该代码段是一个0特权级非一致性、可读、未访问段。

数据段描述符

当S标志位置位(1),Type标志位的最高位置位(0)时,表示这个段描述符描述的段是一个数据段。Type标志位的第2位则进一步描述这个段的功能:

对于表中Type的另外三个标志位E(扩展方向)、W(可读写)、A(已访问)的说明如下:

  • E(Expansion-direction,扩展方向)。此标志位指示数据段的扩展方向,E=1表示向下扩展,E=0表示向上扩展。通常情况下数据段向上扩展。
  • W(Write-enable,可读写)。它记录着数据段的读写权限,W=1表示可读可写,W=0表示只读不可写
  • A(Accessed,已访问)。其功能与代码段描述符中的A标志位类似,记录数据段是否被访问过。

举个栗子,还是这个代码:

示例代码中的数据段描述符LABEL_DESC_DATA32的值为0x00CF92000000FFFF,可根据描述符功能表拆解:

可以看出,这是一个覆盖了整个线性地址空间(4GB)的数据段。而且是一个0特权级非一致性(数据段都是非一致性)的向上扩展、可读写、未访问的数据段。

系统段描述符

如果段描述符的S标志位复位(0),则表示该描述符为一个系统段描述符。系统段描述符的Type字段则进一步定义了12种类型的系统段描述符

这里先主要介绍LDT段描述符、TSS描述符、调用门描述符三种系统段描述符。

LDT段描述符

上面曾介绍过LDT局部段描述符表。那么LDT段描述符则是记录了段描述符表的位置、长度和访问权限等信息。

TSS段描述符

TSS(Task State Segment)任务状态段。任务状态段是x86架构下一个保存任务信息的数据结构,被操作系统内核用于任务管理。TSS任务状态段主要用于任务切换(或特权级切换)时,保存处理器的寄存器信息以及切换至对应特权级的特权级栈空间,从而使得任务返回时可以还原执行现场。

其中的B标志位(Busy)表示任务是否处在“忙”状态,一个处于“忙”状态的任务表明其正在运行或挂起

TSS描述符只能放在GDT中,不能放在LDT或IDT。而任务寄存器(Task Register,TR)与LDTR寄存器的结构非常相似,只不过TR任务寄存器保存的是TSS段选择子和TSS段描述符的信息。

如下图是一个32位TSS任务状态段的内部结构:

TSS任务状态段被分为动态区域和静态区域两个部分

动态区域:

静态区域:

当任务在切换过程中被挂起时,处理器会将执行现场保存在动态区域中。而并不会更新(改变)静态区域中的内容。

对于处理器级别的任务切换而言,只有下面的四种情况:

  • 当前程序通过JMP或CALL指令跳转GDT中的TSS描述符时,任务切换将会发生

  • 当前程序通过JMP或CALL指令跳转至GDT或LDT中的任务门段描述符时,任务切换将会发生

  • 如果一个IDT表项使用任务门描述符来保存中断或异常处理程序,那么在触发中断或者异常时,任务切换将会发生。

  • 如果当前任务的EFLAG.NT标志位被置位(1),那么在执行IRET指令时任务切换将会发生

    嵌套任务标志NT

    (Nested Task)

    嵌套任务标志NT用来控制中断返回指令IRET的执行。具体规定如下:

    当NT=0,用堆栈中保存的值恢复EFLAGS、CS和EIP,执行常规的中断返回操作;

    当NT=1,通过任务转换实现中断返回。

除了上述四种处理器级别的硬件级任务切换方法外,操作系统还可以通过软件的方式实现任务切换。例如Linux内核,当一个CPU从用户态切换到内核态时,它就从TSS中获取内核态堆栈的地址,而寄存器等硬件上下文则通过软件手动的方式保存于栈或者PCB中(看了一下源码,具体的数据结构应该是task_struct->thread_struct

调用门描述符

调用门用于在不同特权级之间实现受控的程序控制转移。调用门描述符的位功能说明如下,主要定义了目标代码段的段选择子、段内偏移和一些属性信息

6.4.3 保护模式的中断/异常处理机制

中断/异常用于实时监控操作系统、处理器、程序的运行。当处理器捕获到中断/异常时,会强制挂起当前任务/程序,转而去执行中断/异常处理程序,因此其实可以直白地将中断/异常处理程序当作是一种服务或者函数。当处理程序执行完毕,处理器会唤醒被挂起的工程或任务,使程序不失连续性地执行,就仿佛从未发生过中断/异常一样。

中断可以来自一个外部硬件设备请求,或者int n指令。而异常只能在检测到处理器执行出现错误或者遇到异常时触发,例如保护性异常、页错误异常、机器内部错误等等。

可以使用数字对统一类型的中断/异常进行标识,这个标识数字称之为中断向量号,向量号的取值范围是0-255,其中0-31号向量被Intel处理器作为异常向量号或保留使用,剩余的32-255号向量将作为中断向量号供用户使用。

根据向量号可以从IDT(Interrupt Descriptor Table,中断描述符表)中索引出对应的门描述符,根据索引出的门描述符即可找到对应的中断/异常处理程序的入口。IDT中断描述符表是一个门描述符数组,它借助门描述符将中断/异常向量号与处理程序关联起来,每个门描述符占8字节。

与GDTR寄存器的作用类似,可以通过IDTR寄存器得到IDT的基地址及长度。在使用IDTR之前,同样需要使用LIDT指令,将IDT的32位线性基地址和16位长度加载进IDTR寄存器中,LIDT是特权指令,只能在CPL=0时使用。通常情况下IDT创建于操作系统初始化的过程。

下图就是通过IDTR找到IDT,从IDT中找到对应的门描述符,从而得到目标代码段的段选择子和段内偏移,再使用段选择子从GDT/LDT中索引到对应的目标段描述符,从而得到目标段的线性基地址,加上段内偏移即最终找到对应中断处理程序入口线性基地址的过程:

IDT的表项由门描述符组成,有三类:陷阱门描述符、中断门描述符、任务门描述符。其中,Linux内核只使用了陷阱门和中断门,而在IA-32e模式中也取消了任务门描述符这一功能,因此,这里只探究一下陷阱门和中断门的描述符

陷阱门描述符、中断门描述符

中断门描述符(Interrupt Gate Descriptor)和陷阱门描述符(Trap Gate Descriptor)在位图结构上和调用门描述符相似,都包含了一个远跳转地址(段选择子和段内偏移),这个远跳转地址即是对应中断/异常处理程序的入口地址。

陷阱门和中断门描述符的位图如上图,其中D标志位用于指示门描述符的数据位宽。中断门描述符和异常门描述符的不同之处主要体现在处理器对EFLAGS.IF标志位的处理上。当处理器通过中断门描述符执行中断/异常处理程序时,处理器会复位IF标志位以防止其它中断请求干扰当前中断处理程序的执行 处理器会在随后执行的IRET指令中,还原保存在栈中的EFLAGS寄存器(包括IF标志位)值。然而,当处理器通过陷阱门描述符执行中断/异常处理程序时,将不会对IF标志位进行操作(不会复位IF标志位)

中断允许标志IF

(Interrupt-enable Flag)

CPU外部的不可屏蔽中断所发出的中断请求,以及CPU内部产生的中断请求。具体规定如下:

当IF=1时,CPU可以响应CPU外部的可屏蔽中断发出的中断请求

当IF=0时,CPU不响应CPU外部的可屏蔽中断发出的中断请求

CPU的指令系统中也有专门的指令来改变标志位IF的值

6.4.4 保护模式的页管理机制

保护模式下,当开启了分页管理机制(CR0.PG=1)后,线性地址需要通过页管理机制转换成物理地址。

这一转换过程需要经过多级页表的索引。页表中不仅保存着目标物理页的基地址,还记录着访问物理页的权限以及缓存类型等属性信息。

保护模式支持三种页管理模式,分别是32位分页模式、PSE模式和PAE模式。

32位分页模式

32位分页模式是保护模式的基础分页模式。在32位分页模式下,线性地址位宽与物理地址位宽同为32位,32位分页模式并未扩展物理地址的寻址能力(只能寻址4GB)。

从上图可以看出,其将一个32位的线性地址分成3部分,10位用于从页目录表中索引页表,10位用于从页表中索引物理页,12位用于表明物理页中的偏移offset。

如上图所示,32位分页模式采用两级页表进行索引。第一级是页目录表PDT(Page Directory Table),其表项页目录项PDE(Page Directory Entry)存储着页表的物理地址。第二级则是页表PT(Page Table),其表项页表项(Page Table Entry)存储着物理页的物理地址。其中,页目录表的物理地址存放在CR3控制寄存器中。

在CR3控制寄存器、页目录表项和页表项中,除了包含目标物理页的物理地址,还包含了控制访问权限及缓存类型等属性信息。如下图所示:

对于各标志位的解释如下:

这些标志位描述了页管理模式的大多数功能。其中,U/S标志位用于限定物理页的访问模式,超级模式只允许特权级为0、1、2的程序访问,而用户模式则允许任何特权级的程序访问。

PSE模式

PSE模式(Page Size Extension)用于使能更大size的页(4MB)。置位CR4.PSE标志位来开启PSE模式,并且页目录项的第7位(PS标志位)为1。如果处理器还支持PSE-36模式,那么开启PSE的同时也会开启PSE-36模式。PSE-36模式能够让处理器在使用超过4GB的物理内存,进而将原本的32位物理地址寻址能力提升至36位乃至40位,寻址位数取决于处理器的最高物理可寻址宽值MAXPHYADDR。由于PSE模式仍然处于保护模式中,所以其线性地址的寻址能力仍然是4GB,扩展的是物理地址的寻址位宽。

如下图描绘了PSE模式的地址映射关系:

如上图,PSE模式只使用一级页表,从而将线性地址的页内偏移字段扩展到了22位,即使得页的大小可以扩展到了4MB。

通过CPUID.80000008h:EAX[7:0]可获得处理器最高物理可寻址位宽值MAXPHYADDR,目前Intel桌面级处理器普遍支持36位物理地址位宽(64GB),服务器级处理器普遍支持40位物理地址位宽(1TB)。现在,52位物理地址位宽是64位体系结构的最大值,但没有一款处理器可以支持这一物理地址位宽。MAXPHYADDR并不代表硬件平台有那么多物理内存可以使用,还有结合BIOS中断服务程序 INT15h, AX=E820h提供的物理内存信息。

下图为PSE-36模式的页表项位功能。可以看到,PSE-36模式对物理地址位宽的扩展是通过对页目录表项中 物理页基地址的扩展实现的,如下图,物理页基地址占了表项的18bit,那么物理可寻址位宽上限为40位(18位物理页基地址+22位页内偏移),即使MAXPHYADDR值超过40,处理器也只能使用低40位进行物理内存寻址。

PAE模式

PAE(Physical Address Extension物理地址扩展)模式,将32位线性地址从原有的耳机页表映射扩展为三级,每个页表项也从32位增长到了64位,映射关系如下图:

可以看到,PAE在原页目录表PDT和页表PT的基础上新增了一个页目录指针表(Page directory),线性地址的最高2位用于在PDPT中索引出PDT,因此PDPT只有四个表项。

PAE模式的页表项功能位如下图所示,最高可以支持52位物理可寻址位宽,但是实际物理可寻址位宽还是参考MAXPHYADDR值:

上图可以看到新增了个XD标志位(Execution Disable,禁止执行标志位),XD标志位用于禁止处理器从物理页中取指令。通过CPUID.80000001h:EDX[20].XD可以查询是否支持此功能。在支持XD功能的前提下,置位IA32_EFER.NXE标志位(IA32_EFER寄存器位于MSR寄存器组)将开启XD功能,否则XD位保留且必须置为0。

6.4.5 保护模式的地址转换过程

其实可以看出,段管理机制比页管理机制复杂很多,保护模式下这两种管理机制看起来占有的比重非常不匀称。保护模式的段管理机制非常复杂,不但会影响处理器的性能,同时也加重了编程的复杂度。为了更高效地执行程序以及简单的编程逻辑,IA-32e模式(64位模式)在简化段管理机制的同时,还深化了页管理机制。而在Linux内核中,将所有段基地址都设为0且段长度覆盖整个线性地址空间,使得Linux内核中的虚拟地址和线性地址在数值上是一样的,从而着重关注页管理机制。

6.5 IA-32e模式

IA-32e是Intel为64位处理器设计的全新运行模式,也被叫做长模式。它包括兼容模式和64位模式两种子模式。顾名思义,兼容模式就是兼容32位的保护模式的,但是在兼容模式下触发系统异常或中断时,处理器仍需切换至64位模式才能处理。

虽然IA-32e模式的线性地址位宽64位,但其线性寻址能力只有48位,低48位用于线性地址寻址,高16位将作为符号扩展(将第47位数值扩展到第63位,全0或全1),这种地址格式被称为Canonical地址。

如下图所示,IA-32e模式中只有Canonical地址空间可用,而Non-Canonical型空间(高16位和第47位不同,不符合Canonical格式)是不可用的。如果程序试图访问Non-Canonical地址空间会触发#GP或#SS等异常。

IA-32e模式的寻址过程如下图所示

0%