分页机制的原理:

分页机制的作用有二,将连成一片的线性地址转换为物理地址,用大小相等的页代替大小不等的段。

android 分页计数器 分页器工作原理_页表项

代码段和数据段在逻辑上被拆分成以页为单位的小内存块,这时的虚拟地址不能存放任何数据,接着操作系统开始为虚拟内存页分配真实的物理内存页,就完成了虚拟地址到物理地址的映射。

为了节省页表空间,将32位地址分为高低两部分,低地址部分为内存块大小,高地址部分是内存块数量,内存块数量×内存块大小=4GB。

一级页表:

将线性地址转换为物理地址的方法如下:

在分页机制打开,我们需要将页表地址加载到控制寄存器cr3中,保存的是物理地址,以后通过cr3我们就可以获取到页表的地址。一个页表项对应一个页,虚拟地址的高20位作为页表项的索引,由于每个页表项占用4字节,所以这高20位的索引值乘以4然后再加上cr3中的页表地址,得到的结果即为该页表项的物理地址。上述我们已经可以定位到页表项的物理地址了,那么下面就是如何从页表项中获取我们需要访问的物理地址。从页表项中取回实际要访问的物理地址,然后将其与虚拟地址的低12位相加,即可得到我们需要访问的物理地址。由于每个页固定位4KB,所以虚拟地址的低12位正好可以表示页内偏移。

mov ax, [0x1234]

android 分页计数器 分页器工作原理_物理地址_02

 

 

假设我们的段基址为0x0,段内偏移为0x1234,CPU的段部件通过计算获得虚拟地址0x1234。由于我们假设分页机制已经打开,所以虚拟地址0x1234被送入页部件,页部件首先发现高20位为0x00001,即页表项的索引为1,将该索引值乘以4然后加上cr3中的物理地址便可得到页表项的物理地址,从该物理地址处读取页表项映射的物理地址为0x9000,虚拟地址的低12位为0x234,即页内偏移地址为0x234,将页表项映射的物理地址为0x9000和页内偏移地址相加,所得的和0x9234即我们需要访问的真实物理地址。

二级页表:

一级页表是将1M个页放置到一张页表中,二级页表是将1M个页平均放置到1K个页表中,每个页表包含1K个页表项,每个页表项4字节,即二级页表这个大小恰好是4KB大小,即一个页。

由于我们将一张页表拆成了多张页表存放,因此需要一个统一管理这些页表的地方,该地方的名称叫页目录表,页目录表和页表以及真实物理空间的关系如下图:

android 分页计数器 分页器工作原理_页表项_03

 

在页表项中分配的物理地址是无规律可循的,操作系统负责进行分配和释放。

二级页表获取物理地址的方法如下:

二级页表需要将虚拟地址拆解成3部分,分别为

高10位:用来定位页目录表中的一个页目录项(页目录项中包含页表的物理地址)

中间10位:用于在某个页表中定位页表项

低12位:页内偏移量

具体的转换过程如下:

段部件生成虚拟地址,用虚拟地址的高10位乘以4,再加上页目录表的物理地址,便是页目录项的物理地址,读取该物理地址处的内容,获得页表的物理地址。用虚拟地址的中间10位乘以4,再加上一步获得的页表的物理地址,便是页表项的的物理地址,读取页表项的内容,便可从页表项的数据结构中获取我们需要访问的物理地址,将该物理地址再加上虚拟地址的低12位,便是最终我们要访问的物理地址。

通过页目录项我们可以知道页表项的物理地址,但是我们怎么知道页目录项的物理地址呢?

答案还是cr3寄存器,由于我们开启二级页表以后,可以在页目录项中获取页表的位置,因此cr3寄存器会存储我们页目录表的地址,因此CR3寄存器也称为页目录基址寄存器,结构如下图:

android 分页计数器 分页器工作原理_物理地址_04

 

分页机制虽然足够灵活,但是却多了很多转换的步骤,这些步骤对于CPU来说还是比较麻烦的,如何可让CPU通过一个虚拟地址直接获取到物理地址呢,不再去查页目录项查页表呢?答案就是缓存。

快表TLB

这个缓存的名称就是快表TLB,结构如下图:

 

android 分页计数器 分页器工作原理_页表项_05

 

 

TLB中存储的是虚拟地地址高20位到物理地址高20位的映射关系。由于TLB是高速缓存,容量较小,因此只有页表项中标志位P为1的页表项才有资格在TLB中,如果TLB被装满,需要将很少使用的条目换出。TLB的维护也是由操作系统完成,关于TLB的更新主要由以下两种方法:

重新加载cr3寄存器,会重新加载整个TLB

使用invlpg指令对指定的缓存条目进行刷新