在正式开始讨论实模式与保护模式下分段与分页机制前,先介绍一下几个专有名词。
8086
Intel8086是英特尔公司于1978年发表的第一款16位微处理器。所有的内部寄存器以及内外部数据总线都是16位宽,因此是完全的16位微处理器。有20位外部地址总线,所以物理地址的空间为1MiB,即:
由于内部寄存器是16位,所以对1M地址空间寻址时采取段寻址方式,即:
8086寄存器组包括:
主寄存器:
- AX(累加器),有高八位AH和低八位AL
- BX(基址寄存器),有高八位BH和低八位BL
- CX(计数寄存器),有高八位CH和低八位CL
- DX(数据寄存器),有高八位DH和低八位DL
变址寄存器:
- SI(源变址寄存器)
- DI(目的变址寄存器)
- BP(基址指针寄存器)
- SP(堆栈指针寄存器)
程序计数器:
- IP(指令指针寄存器)
段寄存器:
- CS(代码段寄存器)
- DS(数据段寄存器)
- ES(附加段寄存器)
- SS(堆栈段寄存器)
状态寄存器:
- PWD(标志寄存器)
实模式
实模式是早期8086CPU唯一的操作模式,以及80286后80x86系兼容CPU的操作模式,实模式的特性是一个20比特的区段存储器地址空间。
所有的x86CPU都是在实模式下引导,来确保传统操作系统的兼容性。为了使用保护模式的特性,要由程序主动地切换到保护模式。
保护模式
保护模式是一种80286系列和之后的x86兼容CPU的运行模式。保护模式有一些新的特性,如存储器保护、标签页系统以及硬件支持的虚拟内存。
随着CPU的发展,CPU地址线从20根增加到32根。由此,可访问的内存空间变为4GB,寄存器位数变为32位。实模式下内存地址计算方式不再有效,所以需要实现更大地址空间,更灵活安全的寻址。
在32位保护模式下,状态和控制寄存器除了EFLAGS和EIP,还有四个32位控制寄存器,分别为:CR0、CR1、CR2、CR3。
CR0:
- PE 保护允许位:PE = 1时,启用保护模式,否则启用实模式。
- MP 监控协处理位:与第3位TS(任务转换位)一起决定。MP = 1时启用TS标志控制等待/FWAIT指令的交互。
- TS 任务转换位:当一个任务转换完成后,自动置1。当TS为1时,协处理器被禁用。
- EM 模拟协处理器位:若EM为1,则协处理器禁用。
- ET 处理器拓展类型位:保存处理器拓展类型信息。
- PG 分页标志位:当PG为1时开启分页,否则禁止分页。在开启这个标志前应确保已开启PE标志
- WP 写保护标志位 WP = 1时,处理器禁止0特权级程序向用户级只读页面执行写操作。
CR1:
未定义的控制寄存器,供将来的处理器使用。若尝试访问,CPU会抛出#UD异常。
CR2:
页故障线性地址寄存器,保存最后一次出现页故障的全32位线性地址。
CR3:
页目录基址寄存器,保存页目录表的物理地址,页目录表首地址总是以4KB为边界对齐,因此低12位应该恒为0。
CR0、CR2、CR3对于分页机制的意义:
系统在刚上电时,处理器被复位为PE = 0、PG = 0(实模式状态),以允许引导代码在启用分段和分页机制前能初始化这些寄存器和数据结构。
PE = 1、PG = 0时,此时为保护模式不开启分页的情况,线性地址 = 物理地址。
PE = 1、PG = 1时,此时为保护模式下的分页模式。
CR2用于报告页异常信息,异常处理程序可以根据CR2的内容确定哪一个页面引发了异常。
CR3又叫做PDBR,存放页目录的物理地址。
正片开始
实模与保护模式下的逻辑地址
实模式下的逻辑地址一般是这样的形式:
16 bits 段寄存器:16 bits 段内偏移
而32位保护模式下的逻辑地址一般是这样的形式:
16 bits 段选择子:32 bits 段内偏移
保护模式下的分段
段选择子
在保护模式下,原来实模式16位的段寄存器不再存储段基地址,而是称为段选择器(段选择子),其中高13位(index)为指向段描述符表的索引。TI(Table Indicator)用于标识描述符表的类型,0代表GDT,1代表LDT。RPL定义了请求的特权级别,从0~3,0为最高权限,3是最低权限。
GDT(全局描述符表)
全局描述符表是用于界定不同内存区域特征的数据结构,它位于内存中。它的每一个条目描述及规定了不同内存分区的特征,如基地址、大小、访问权限、可执行、可写等,每一个条目又称为段描述符,下图为GDT中的一个描述符:
访问GDT时,需要用到段选择子和段偏移值。首先处理器使用段选择子为索引查找GDT的条目,当成功查找到后,处理器做一系列检查,包括段偏移值尝试访问的区间是否在次内存分段内,以及分级保护域的权限(是否有权访问)。
其中,第8位到第15位作为段描述符的访问控制字节,分别对应:
- Type 段类型
- S(DT) 描述符类型
S = 0时表示系统段描述符,S = 1时表示数据或代码段描述符。
- DPL 描述符特权级
Ring 0-3。
- P 段存在标志
其中,第16到第23位为段描述符的粒度字节,分别对应:
- Limit 段限长
在这个字节中的高四位,和段描述符中其他的低16位一起组成20位长。
- G 粒度
当G = 0时,则段限长的单位为字节,20位最多表示1MB。若此时限长为0,则可以表示段长度为1个字节,即可以存储1字节内容;
当G = 1时,段长度的单位为4KB,20位可以表示4GB。若此时限长为0,则可以表示段长度为4KB,即可以存储4KB内容。
- D/B 默认操作大小/默认栈指针大小和/或上界限标志
对于32位代码和数据段,该标志位应该总是为1;对于16为代码和数据段,该标志应该总是为零。
- A 可用和保留位
可供系统软件使用,而位21为保留位(图中灰色部分),应该总是为零。
另外,在实模式下,段基址左移4位拓展为20位,所以必然是0x10(十进制的16)的倍数。保护模式下却不然,段基地址长度为32位,无需拓展,自然也不用对齐。
GDTR
GDT是位于内存中的数据结构,通过段选择子访问GDT表条目可以得到段描述符,并进一步得到基地址。那么该怎么访问GDT呢?
32位CPU提供了一个48位寄存器——GDTR寄存器,高32位用于存储GDT的基址,低16位用于存放GDT的限长。
分段机制总结
通过以上三种结构,我们可以理解保护模式下的分段机制:
1.根据指令性质确定段寄存器,如转移指令地址在代码段,而取数据指令在数据段。
2.通过GDTR找到GDT的基地址,用段选择子作为索引索引出段描述符,以及段基址。
3.越界检查、越权检查。
4.段描述符的基地址加上段偏移地址即为线性地址(当未启用保护模式下的分页机制时,线性地址等价于物理地址)。
保护模式下的分页
页
在32位x86保护模式下,线性地址被分割成4KB大小的页面。物理地址也同样被分割为4KB固定大小的块,称为页框(页帧),页面可以映射到页框里。
页表
页表是一种特殊的数据结构,放在系统空间的页表区,存放逻辑页于物理页帧的对应关系。每一个进程都拥有一个自己的页表,PCB表中有指针指向页表。
通常页表是一个4KB的数据结构,每一个页表项为4B,所以每个页表中可以存储1024个页表项。页表项的结构如下图:
其中,高20位为页基地址。因为每一页为4KB,页面和页帧都要以4KB对齐。4KB=0x1000字节。所以在计算的时候,要将页基地址乘以0x1000(即左移12位),再加上偏移地址,得到物理地址。
其他字段分别代表的含义为:
- P
P = 1时,该页存在。
- R/W
该字段为1的时候,代表可写;否则只读。在CR0标志位设置成实模式时不起效。
- U/S
该字段为1时,代表页面为用户态页面,否则为内核态页面。用户模式的代码不能读写内核态页面。
- PWT/PCT
CPU保留使用
- A
由CPU设置,该字段为1时,代表该页被访问过。
- D
该字段为1时,代表该页被写过。
- Avail
留作内核使用。
页目录
对于4GB的内存空间,倘若按照4KB每页进行分页,那么一共可以分为1024个页面,刚好作为一个页表的所有页表项。每个页表项对应32位(4B)的空间,则存储一张这样的页表共需要4MB的空间。
倘若物理内存是GB为单位,4MB看起来并不是很大。但若是运行在只有16MB大小地址空间下,4MB则是一个相对比较大的开销。所以我们引入了多级页表的概念:页目录——可以看成是页表的页表。
所以,页目录的每一个表项应该包含页表的基地址,页目录项如下图所示:
可以看到,与页表项很相似。第12位至第31位包含了页表的基地址。
那么我们来计算一下,一个页目录包含1024个页目录项,每个页目录项为4B,则页目录需要占用4KB的空间。对于4GB的线性地址,刚好可以对应1024个页表+一个页目录,那么总共需要的空间为4MB+4KB。这岂不是比原来的4MB还大了吗?其实并不然,在一个进程中,实际使用的内存远没有4GB这么大,在两级页表的映射下,我们只需要映射需要的地址,大大简省了内存。
CR3寄存器
CR3寄存器的结构如上图,它是一个32位寄存器。12位到31位包含了页目录的基地址。
分页机制总结
了解了页目录、页表和CR3寄存器的概念后,线性地址映射到物理地址的过程就很清晰了。
如上图所示。线性地址是一个32位的地址,高10位代表在页目录中的偏移(10位刚好对应页目录中1024个页目录项),中间10位代表在页表中的偏移(同理,10位代表页表中1024个页表项的偏移),低12位代表页内偏移。
所以,从线性地址转换为物理地址的具体步骤如下:
- 首先从CR3寄存器中取得页目录的基地址(20位,拓展成32位)。
- 以线性地址的高10位为索引,取得对应的页目录项,并进一步取得页表的基地址(20位,拓展为32位)。
- 以线性地址的中间10位为索引,在页表中取得相应的页表项,并进一步取得页的基地址(20位,拓展为32位)。
- 将该基地址左移12位,并加上线性地址的低12位(对应页内偏移地址),得到物理地址。
所以,在32位开启了分段与分页机制的保护模式下,由逻辑地址到物理地址的全流程如下:
因作者水平有限,如有谬误,还望指出。
参考资料: