1、CPU工作模式
1)实模式:8086的寄存器只有16位,我们也习惯于称8086的工作模式为1·6位模式。后续的CPU为了保持兼容性,在芯片上了电以后,还必须运行于16位模式之下。这种模式还有个正式的名字叫做实模式。在实模式下,程序是不能通过内存管理单元间接访问物理内存的,而是直接访问物理内存。访问方式是通过“段寄存器:段内偏移”这种形式,计算方式为:
物理地址 = 段寄存器 << 4 + 段内偏移
实模式的特点是直接操作物理内存,但是内存管理容易出错,要十分小心,代码编写和调试都很困难。
2)保护模式:经过十年的发展,X86 CPU 迎来了历史上使用最广泛、影响力最大的 32 位 CPU,这就是 i386 芯片。i386 与 8086 的一个很大的不同,就是它采用了全新的保护模式。这个体现在,i386 中的段式管理机制,相比 8086 发生了重大变化;同时,i386 芯片在段式管理的 基础上,还引入了页式管理。i386 在完成各种初始化动作以后,就会开启页表,从此程序员就不必再直接操作物理内存 的地址空间了,代替它的是线性地址空间。而且由于段和页都能提供对内存的保护,安全 性也得到了提升,所以这种工作模式被称为保护模式(Protection Mode)。i386 的保护 模式是一种段式管理和页式管理混合使用的模式。比实模式,i386 中的保护模式, 采用了页式管理,但它没有彻底放弃 8086 的段式管理,而是将段寄存器中的值由段基址 变成了段选择子。段选择子本质是 GDT 表的下标值,段基址都转移到 GDT 中去了。
2、GDT和LDT
TSS(见图2-1)定义了任务执行环境的状态。它包括通用寄存器、段寄存器、EFLAGS 寄存器、EIP 寄存器以及带有三个堆栈段(每个特权级别一个堆栈)的堆栈指针的段选择器的状态。 TSS 还包括与任务关联的 LDT 的段选择器以及分页结构层次结构的基地址。
保护模式下的所有程序执行都发生在任务(称为当前任务)的上下文中。当前任务的 TSS 的段选择器存储在任务寄存器中。切换到任务最简单的方法是调用或跳转到新任务。这里,新任务的 TSS 的段选择器在 CALL 或 JMP 指令中给出。在切换任务时,处理器执行以下操作:
1) 将当前任务的状态存储在当前TSS中。
2) 将新任务的段选择器加载到任务寄存器中。
3.)通过GDT 中的段描述符访问新的TSS。
4)将新任务的状态从新 TSS 加载到通用寄存器、段寄存器、LDTR、控制寄存器 CR3(分页结构层次结构的基地址)、EFLAGS 寄存器和 EIP 寄存器中。
5)开始执行新任务。
还可以通过任务门访问任务。任务门与调用门类似,不同之处在于它提供(通过段选择器)对 TSS 而不是代码段的访问。
外部中断、软件中断和异常都是通过中断描述符表(IDT)来处理的。IDT 存储门描述符的集合,提供对中断和异常处理程序的访问。与 GDT 一样,IDT 也不是段。 IDT 基址的线性地址包含在 IDT 寄存器 (IDTR) 中。IDT 中的门描述符可以是中断、陷阱或任务门描述符。为了访问中断或异常处理程序,处理器首先通过 INT n、INTO、INT3、INT1 或 BOUND 指令从内部硬件、外部中断控制器或软件接收中断向量。中断向量提供了 IDT 的索引。如果所选择的门描述符是中断门或陷阱门,则以与通过调用门调用过程类似的方式访问关联的处理程序过程(不会发生任务切换)。如果描述符是任务门,则通过任务切换来访问处理程序。
系统架构支持内存的直接物理寻址或虚拟内存(通过分页):当使用物理寻址时,线性地址被视为物理地址。使用分页时:所有代码、数据、堆栈和系统段(包括 GDT 和 IDT)都可以进行分页,并且仅将最近访问的页面保存在物理内存中。物理内存中页(有时称为页框)的位置包含在分页结构中,这些结构驻留在物理内存中。
分页结构层次结构的基本物理地址包含在控制寄存器 CR3 中。分页结构中的条目确定页框基址的物理地址、访问权限和内存管理信息。为了使用这种分页机制,线性地址被分成几部分。这些部分提供了分页结构和页框的单独偏移。系统可以具有单个或多个分页结构层次结构。
3、IA-32e 模式下的内存管理
在IA-32e模式下,物理内存页由一组系统数据结构管理。在兼容模式和 64 位模式下,都使用四级或五级系统数据结构(参见第 4 章“分页”)。其中包括:
• 页映射级别 5 (PML5) — PML5 表中的条目包含 PML4 表基址的物理地址、访问权限和内存管理信息。 PML5表的基物理地址存储在CR3中。 PML5 表仅用于 5 级分页。
• 页映射级别4 (PML4) — PML4 表中的条目包含页目录指针表基址的物理地址、访问权限和内存管理信息。对于4级分页,只有一张PML4表,其基本物理地址存储在CR3中。
• 一组页目录指针表——页目录指针表中的条目包含页目录表基址的物理地址、访问权限和内存管理信息。
• 页目录集——页目录表中的条目包含页表基址的物理地址、访问权限和内存管理信息。
• 页表集——页表中的条目包含页框的物理地址、访问权限和内存管理信息。
4、系统寄存器
为了帮助初始化处理器和控制系统操作,系统架构在 EFLAGS 寄存器和几个系统寄存器中提供了系统标志:
• EFLAGS 寄存器中的系统标志和 IOPL 字段控制任务和模式切换、中断处理、指令跟踪和访问权。
• 控制寄存器(CR0、CR2、CR3 和CR4)包含用于控制系统级操作的各种标志和数据字段。这些寄存器中的其他标志用于指示操作系统或执行程序内对特定处理器功能的支持。
• 调试寄存器(图2-1 中未显示)允许设置断点以用于调试程序和系统软件。
• GDTR、LDTR 和IDTR 寄存器包含其各自表的线性地址和大小(限制)。
• 任务寄存器包含当前任务的TSS 的线性地址和大小。
• 特定于型号的寄存器。
5、内存管理寄存器
GDTR 寄存器保存基地址(保护模式下为 32 位;IA-32e 模式下为 64 位)和 GDT 的 16 位表限制。基地址指定GDT的种字节偏移量为0的线性地址(类似于数组首地址, 下同);表限制指定表中的字节数。LGDT 和 SGDT 指令分别加载和存储 GDTR 寄存器。处理器加电或复位时,基地址设置为默认值 0,限制设置为 0FFFFH。作为保护模式操作的处理器初始化过程的一部分,必须将新的基地址加载到 GDTR 中。
LDTR 寄存器保存 16 位段选择器、基地址(保护模式下为 32 位;IA-32e 模式下为 64 位)、段限制和 LDT 的描述符属性。基地址指定LDT段中字节偏移量为0的线性地址;段限制指定段中的字节数。LLDT 和 SLDT 指令分别加载和存储 LDTR 寄存器的段选择器部分。包含LDT的段必须在GDT中具有段描述符。当 LLDT 指令在 LDTR 中加载段选择器时:LDT 描述符中的基址、限制和描述符属性会自动加载到 LDTR 中。当任务切换发生时,LDTR 会自动加载新任务的 LDT 的段选择器和描述符。在将新的LDT信息写入寄存器之前,LDTR的内容不会自动保存。处理器加电或复位时,段选择器和基地址设置为默认值 0,限制设置为 0FFFFH。
任务寄存器保存 16 位段选择器、基地址(保护模式下为 32 位;IA-32e 模式下为 64 位)、段限制以及当前任务 TSS 的描述符属性。段选择器引用 GDT 中的 TSS 描述符。基地址指定TSS中字节偏移量为0的线性地址;段限制指定 TSS 中的字节数。 LTR 和STR 指令分别加载和存储任务寄存器的段选择器部分。当 LTR 指令在任务寄存器中加载段选择器时,TSS 描述符中的基址、限制和描述符属性会自动加载到任务寄存器中。处理器加电或复位时,基地址设置为默认值 0,限制设置为 0FFFFH。当任务切换发生时,任务寄存器会自动加载新任务的 TSS 的段选择器和描述符。在将新的 TSS 信息写入寄存器之前,任务寄存器的内容不会自动保存。
6、内存管理
IA-32架构的内存管理设施分为两部分:分段和分页。分段提供了一种隔离各个代码、数据和堆栈模块的机制,以便多个程序(或任务)可以在同一处理器上运行而不会相互干扰。分页提供了一种实现传统的按需分页虚拟内存系统的机制,其中程序执行环境的各个部分根据需要映射到物理内存中。分页还可用于提供多个任务之间的隔离。当在保护模式下运行时,必须使用某种形式的分段。没有模式位可以禁用分段。然而,分页的使用是可选的。
分段提供了一种将处理器的可寻址内存空间(称为线性地址空间)划分为更小的受保护地址空间(称为段)的机制。段可用于保存程序的代码、数据和堆栈,或保存系统数据结构(例如 TSS 或 LDT)。如果处理器上运行多个程序(或任务),则可以为每个程序分配其自己的一组段。然后,处理器强制执行这些段之间的边界,并确保一个程序不会通过写入另一个程序的段来干扰另一个程序的执行。分段机制还允许分段的类型,从而可以限制可以对特定类型的分段执行的操作。
系统中的所有段都包含在处理器的线性地址空间中。要在特定段中定位字节,必须提供逻辑地址(也称为远指针)。逻辑地址由段选择器和偏移量组成。段选择器是段的唯一标识符。除此之外,它还为称为段描述符的数据结构提供描述符表(例如全局描述符表,GDT)的偏移量。每个段都有一个段描述符,它指定了段的大小、段的访问权限和特权级别、段类型以及该段的第一个字节在线性地址空间中的位置(称为段的基地址)段)。逻辑地址的偏移部分被添加到段的基地址上以定位段内的字节。基地址加上偏移量就形成了处理器线性地址空间中的线性地址。
由于多任务计算系统通常定义的线性地址空间远大于一次性包含在物理内存中的经济可行性,因此需要某种“虚拟化”线性地址空间的方法。线性地址空间的虚拟化是通过处理器的分页机制来处理的。分页支持“虚拟内存”环境,其中使用少量物理内存(RAM 和 ROM)和一些磁盘存储来模拟大型线性地址空间。使用分页时,每个段被分为页(通常每个大小为 4 KB),这些页存储在物理内存或磁盘上。操作系统或执行程序维护一个页目录和一组页表来跟踪页面。当程序(或任务)尝试访问线性地址空间中的地址位置时,处理器使用页目录和页表将线性地址转换为物理地址,然后在该地址上执行请求的操作(读或写)。内存位置。如果正在访问的页面当前不在物理内存中,则处理器会中断程序的执行(通过生成页面错误异常)。然后操作系统或执行程序将页面从磁盘读入物理内存并继续执行程序。当在操作系统或执行程序中正确实现分页时,物理内存和磁盘之间的页面交换对于程序的正确执行是透明的。即使是为 16 位 IA32 处理器编写的程序在虚拟 8086 模式下运行时也可以(透明地)进行分页。
多段模型使用分段机制的全部功能来提供对代码、数据结构以及程序和任务的硬件强制保护。这里,每个程序(或任务)都有自己的段描述符表和自己的段。这些段对于其分配的程序来说可以是完全私有的,也可以在程序之间共享。对系统上运行的各个程序的所有段和执行环境的访问均由硬件控制。
访问检查不仅可用于防止引用段限制之外的地址,还可防止在某些段中执行不允许的操作。例如,由于代码段被指定为只读段,因此可以使用硬件来防止写入代码段。为段创建的访问权限信息还可用于设置保护环或级别。保护级别可用于保护操作系统过程免遭应用程序未经授权的访问。
在 64 位模式下,通常(但不是完全)禁用分段,从而创建平坦的 64 位线性地址空间。处理器将 CS、DS、ES、SS 的段基数视为零,从而创建等于有效地址的线性地址。 FS 和 GS 段是例外。这些段寄存器(保存段基址)可以用作线性地址计算中的附加基址寄存器。它们有助于寻址本地数据和某些操作系统数据结构。
7、逻辑地址和线性地址
在保护模式下的系统架构层面,处理器使用两个阶段的地址转换来获取物理地址:逻辑地址转换和线性地址空间分页。逻辑地址由 16 位分段选择器和 32 位偏移量组成。段选择器确定字节所在的段,偏移量指定字节在段中相对于段基地址的位置。线性地址是处理器线性地址空间中的 32 位地址。与物理地址空间一样,线性地址空间也是一个平面(无分段)、 字节的地址空间,地址范围从 0 到 FFFFFFFFH。线性地址空间包含为系统定义的所有段和系统表。要将逻辑地址转换为线性地址,处理器需要执行以下操作:
1)使用段选择器中的偏移量来查找 GDT 或 LDT 中的段描述符,并将其读入处理器。(只有在段寄存器中装入新的段选择器时才需要此步骤)。
2)检查段描述符,检查段的访问权限和范围,确保段可以访问,偏移量在段的限制范围内。
3)将段描述符中的段基地址与偏移量相加,形成线性地址。
如果不使用分页,处理器会将线性地址直接映射为物理地址(即线性地址通过处理器的地址总线输出)。如果对线性地址空间进行了分页,则会使用第二级地址转换将线性地址转换为物理地址。
8、段选择器
段选择器是段的 16 位标识符。它并不直接指向段,而是指向定义该段的段描述符。段选择器包含以下项目: 1)索引(位 3 到 15)— 选择 GDT 或 LDT 中的 8192 个描述符之一。处理器将索引值乘以 8(段描述符中的字节数),并将结果添加到 GDT 或 LDT 的基地址(分别来自 GDTR 或 LDTR 寄存器)。2)TI(表指示符)标志(位 2)— 指定要使用的描述符表:清除该标志将选择 GDT;设置此标志选择当前 LDT。3)请求的特权级别 (RPL)(位 0 和 1)— 指定选择器的特权级别。权限级别的范围为 0 到 3,其中 0 是最高权限级别。
处理器不使用 GDT 的第一个条目。指向 GDT 的该条目的段选择器(即索引为 0 且 TI 标志设置为 0 的段选择器)被用作“空段选择器”。当段寄存器(CS 或 SS 寄存器除外)加载了空选择器时,处理器不会生成异常。然而,当使用持有空选择器的段寄存器来访问内存时,它确实会生成异常。空选择器可用于初始化未使用的段寄存器。使用空段选择器加载 CS 或 SS 寄存器会导致生成一般保护异常 (#GP)。段选择器作为指针变量的一部分对应用程序可见,但选择器的值通常由链接编辑器或链接加载器而不是应用程序分配或修改。
8、段寄存器
为了减少地址转换时间和编码复杂性,处理器提供了用于保存最多 6 个段选择器的寄存器(见图 3-7)。每个段寄存器都支持特定类型的内存引用(代码、堆栈或数据)。实际上,要执行任何类型的程序,至少必须向代码段 (CS)、数据段 (DS) 和堆栈段 (SS) 寄存器加载有效的段选择器。处理器还提供三个附加数据段寄存器(ES、FS 和 GS),可用于为当前正在执行的程序(或任务)提供附加数据段。对于要访问段的程序,该段的段选择器必须已加载到段寄存器之一中。因此,尽管系统可以定义数千个段,但只有 6 个可以立即使用。可以通过在程序执行期间将其段选择器加载到这些寄存器中来使其他段可用。
每个段寄存器都有一个 "可见 "部分和一个 "隐藏 "部分。(隐藏部分有时被称为 "描述符缓存 "或 "阴影寄存器")。当段选择器被加载到段寄存器的可见部分时,处理器也会将段选择器指向的段描述符中的基地址、段限制和访问控制信息加载到段寄存器的隐藏部分。缓存在段寄存器(可见和隐藏)中的信息允许处理器转换地址,而无需花费额外的总线周期从段描述符中读取基地址和权限等信息。在多个处理器访问相同描述符表的系统中,当描述符表被修改时,软件有责任重新加载段寄存器。如果不这样做,段寄存器中缓存的旧段描述符可能会在其内存驻留版本被修改后被使用。有两种加载指令用于加载段寄存器:
1)直接加载指令,如 MOV、POP、LDS、LES、LSS、LGS 和 LFS 指令。这些指令明确引用段寄存器。
2)隐含加载指令,如 CALL、JMP 和 RET 指令的远指针版本,SYSENTER 和 SYSEXIT 指令,以及 IRET、INT n、INTO、INT3 和 INT1 指令。这些指令改变 CS 寄存器(有时还包括其他段寄存器)的内容是其操作的附带部分。
9、段描述符
当段描述符中的S(描述符类型)标志被清除时,描述符类型是系统描述符。处理器识别以下类型的系统描述符:
本地描述符-表(LDT)段描述符。
任务状态段(TSS)描述符。
呼叫门描述符。
中断门描述符。
陷阱门描述符。
任务门描述符。
这些描述符类型分为两类:系统段描述符和门描述符。系统段描述符指向系统段(LDT和TSS段)。门描述符本身就是“门”,它保存指向代码段中过程入口点的指针(调用门、中断门和陷阱门),或者保存TSS的段选择器(任务门)。
段描述符表是段描述符的数组,述符表的长度可变,最多可以包含8192()个8字节描述符。有两种描述符表:全局描述符表(GDT)和 本地描述符表(LDT)。
每个系统必须定义一个GDT,它可以用于系统中的所有程序和任务。但是可以定义一个或多个LDT。例如,可以为正在运行的每个单独的任务定义LDT,或者一些或所有任务可以共享相同的LDT。GDT 本身并不是一个段;相反,它是线性地址空间中的数据结构。 GDT 的基地址和长度必须加载到 GDTR 寄存器中。 GDT 的基地址应在八字节边界上对齐,以便处理器达到最好的性能。 GDT 的限制值以字节表示。与段一样,将限制值添加到基地址以获得最后一个有效字节的地址。限制值 0 只会产生一个有效字节。由于段描述符始终为 8 字节长,因此 GDT 限制应始终小于 8 的整数倍(即 8N – 1)。GDT 中的第一个描述符未被处理器使用。当加载到数据段寄存器(DS、ES、FS 或 GS)时,此“空描述符”的段选择器不会生成异常,但当尝试执行操作时,它总是生成一般保护异常 (#GP)。使用描述符访问内存。通过使用该段选择器初始化段寄存器,可以保证意外引用未使用的段寄存器会生成异常。
LDT位于LDT类型的系统段中。 GDT 必须包含 LDT 段的段描述符。如果系统支持多个LDT,则每个LDT在GDT中必须有一个单独的段选择器和段描述符。 LDT 的段描述符可以位于 GDT 中的任何位置。LDT 通过其段选择器来访问。为了消除访问 LDT 时的地址转换,LDT 的段选择器、基线性地址、限制和访问权限存储在 LDTR 寄存器中。