内存分页机制

为什么要分页

在保护模式中,内存访问使用分段机制——即"段基址:段内偏移地址"的方式,为加强段内存的安全性和可管理性还引入了段描述符的概念对段内存加强管理。但仅仅这样还是不够的,如果应用程序过多,或者内存碎片过多,又或者曾经被换出到硬盘的内存段需要再重新装载到内存,可内存中找不到合适大小的区域,要如何解决?

内存置换

当下大部分操作系统的方案是,将一些进程不常用的内存段换出到硬盘中,腾出内存空间供需要的进程使用。虽然硬盘是比内存还低速的设备,在两个速度不匹配的设备之间进行数据交换对内存来说是一种浪费,但使用合理的置换算法可以减少置换次数。
在保护模式下,内存段的各种属性由段描述符保存,CPU在引用一个段时,都要先查看段描述符。段描述符存放在GDT或LDT中,即使段并不在内存中,即CPU允许在段描述表中已注册的段不在内存中存在。如果该描述符中的P位为1,表示该段在内存中存在,访问过该段后,CPU将段描述符中的A位置1,表示近期访问过该内存段。
段描述符的A位由CPU置1,清0则由操作系统来完成。操作系统每发现该位置1后就将该位清0,同时统计一个周期内该位为1的次数,就可以知道该内存段的使用频率,从而在物理内存不足时找出使用频率最低的段将其换出到硬盘以腾出内存空间给需要使用内存的进程。当段被换出到硬盘后,操作系统将该段的段描述符的P位置0,表示该段已不在内存中。
如上所述,计算机的软件和硬件相互配合完成内存的置换工作。虽然这在一定程度上解决了内存不足的问题,但还是有缺陷。比如物理内存特别小,以至于无法容纳任何一个进程的段,这就没法运行进程,更没法做段的置换工作了。而出现这种问题的本质原因是在目前只有分段的情况下,CPU认为线性地址等于物理地址,而线性地址是由编译器编译出来的,它本身是连续的,所以物理地址也必须要连续才行,但我们可用的物理地址并不连续。因此,为解决这个问题,我们需要解除线性地址与物理地址一一对应的关系,让他们之间重新建立映射——即让线性地址连续而物理地址不连续。在操作系统中实现这种映射的策略就是内存分页机制,相关数据结构就是页表。

分页机制

分页机制其实是建立在分段机制之上的。尽管在保护模式中段寄存器的内容已经是选择子,但选择子最终是为了找到段基址,因此其内存访问的核心机制仍然是“段基址:段内偏移地址”形成的绝对地址,也就是我们所说的线性地址。此线性地址在分段机制下就是物理地址,可以被CPU直接送上地址总线。分段机制下的内存访问如下图所示:
内存分页机制_第1张图片
分页机制建立在分段机制的基础上,因此分页是在分段之后进行的,如下图所示:
内存分页机制_第2张图片
与分段机制不同的是,在打开分页机制的情况下,“段基址:段内偏移地址”经过段部件处理后得到的线性地址就不再是物理地址了,而是虚拟地址,它是逻辑上的,是假的,还不能被送上地址总线,因为CPU必须要得到物理地址。虚拟地址对应的物理地址需要在页表中查找(由页部件自动完成)。
分页机制的作用主要是提供连续线性地址到不连续物理地址的映射,以及用大小相等的页代替大小不等的段。由于分页机制建立在分段机制之上,即使在分页机制下也要先经过逻辑上的分段才行。每加载一个进程,操作系统按照进程中各段的起始范围,在进程自己的4GB虚拟地址空间中寻找可用空间分配内存段。接着操作系统开始为这些虚拟内存分配真实的物理内存页,它查找物理内存中可用的页,然后在页中登记这些物理页地址,这样就完成了分页机制下虚拟页到物理页的映射,每个进程都以为自己独享4GB地址空间。

一级页表

页是地址空间的计量单位,所以线性地址的一页也要对应物理地址的一页。一页大小为4KB,这样一来,4GB地址空间被划分为1M个页,也就是4GB空间中可以容纳1048576个页,页表中自然也要有1048576个页表项,这就是一级页表的构造。内存分页机制_第3张图片
一级页表的线性地址与物理地址间的映射关系是如何的呢?Linux将页表项的32位地址分为两部分,第0-11位用来表示页的大小,可以作为页内寻址;第12-31位用来表示页的数量,可以用来索引一个页。CPU中集成了用于页表寻址的硬件——页部件。
加入一级页表后,地址转换的完整过程如下:将“段基址:段内偏移地址”传入,经段部件处理后生成线性地址,并将其传入页部件进行进一步处理。页部件将传入的线性地址分成高20位和低12位。高20位是页表的物理地址中的偏移地址部分,将其左移2位后与cr3寄存器中页表的物理地址相加后得到页表项所在的位置,从页表项中读取所映射的物理页地址;而低12位则作为物理页的页内偏移地址,将其与物理页地址相加后得到的就是最终转换成的物理地址。总的过程简述起来就是,需要得到页表项所在的物理地址,从中取出物理页的部分地址信息,从而得到物理页。

二级页表

一级页表解决了纯段式内存的缺陷,但自身还不够完善。例如,一级页表中所有页表项都必须要提前建好,原因是操作系统要占用4GB虚拟地址空间的高1GB,用户进程占用低3GB。且每个进程都有自己的页表,进程一多,光是为每个进程的页表分配的物理空间就不少了。
二级页表可以很好地解决上述问题,它在一级页表上再虚拟化了一个数据结构——页目录表,来管理一级页表。因此进程可以根据需要动态创建和释放内存而不需要预先划分好空间,且每个进程都拥有的是页目录表,这样一个进程实体所占空间就进一步缩小了。
无论是几级页表,标准页的尺寸都是4KB,所以4GB线性地址空间一共有1M个标准页。一级页表是将这1M个标准页放置到一张页表中,二级页表则是将这1M个标准页平均放置到1K(1024)个页表中,每个页表包含1K个页表项。这1K个页表则由页目录表来管理。
页目录表的元素——页目录表项可以作为索引得到页表的物理地址。页表和页目录表本身作为一种数据结构本身要占用一定物理内存空间,但页表结构本身与其他数据混布在物理内存中,它们所占用的物理页从外在形式上与其他数据占用的物理页没有什么不同,只有CPU才能“分辨”。页表在建立之初还是相对较整洁的,随着操作系统分配和释放内存的动作越来越多,物理内存的布局就更加零散。
在一级页表中,虚拟地址被拆分成2个部分,而由于二级页表增加了页目录表这个数据结构,因此虚拟地址被拆分成了3个部分。
二级页表中,32位的虚拟地址中,高10位(22-31)用来在页目录表中定位一个页目录表项(PDE),PDE中有页表的物理地址。找到页表后,中间10位(12-21)则用来在页表中定位一个页表项(PTE),PTE中有分配的物理页地址。余下低12位则用于页内偏移量。
由此我们可以得出使用二级页表的系统的完整寻址过程:(1)用虚拟地址的高10位乘4,作为页目录表内的偏移地址,加上页目录表的物理地址得到PDE的物理地址,读取PDE得到页表的物理地址。(2)用虚拟地址的中间10位乘4作为页表内的偏移地址,加上在上一步中得到的页表的物理地址,得到PTE,从PTE中读取到分配的物理页地址。(3)虚拟地址的高10位和中间10位分别是PDE和PTE的索引值,因此它们需要加以转换(左移2位),但低12位不需要左移,它直接与上一步中得到的物理页地址相加即可得到最终转换的物理地址。
内存分页机制_第4张图片

页表项和页目录表项

页表项和页目录表项的结构如下图所示:
在这里插入图片描述
0~11位的含义如下:
P:Present,即存在位。若为1表示该页存在于物理内存中,若为0则表示该表不在物理内存中。
RW:Read/Write,即读写位。为1表示可读可写,为0表示可读不可写。
US:User/Supervisor,意为普通用户/超级用户标识位。为1表示处于User级(0、1、2、3)特权的程序都可以访问该页,为0则表示只有处于Supervisor级(0、1、2)的程序才能访问。
PWT:Page-Level Write-Through,页级通写位。若为1表示此项采用通写方式,即表示该页不仅是普通内存,还是高速缓存,此项与快表有关。
PCD:Page-Level Cache Disable,页级告诉缓存禁止位。若为1表示该页启用高速缓存,为0则表示禁止将该页缓存。
A:Accessed,访问位。若为1表示该页被CPU访问过,由CPU置1,由操作系统清0。
D,Dirty,脏页位。当CPU对一个页执行写操作时,就会设置对应页表项的D位为1,此项仅针对页表项有效,并不会修改页目录表项的D位。
PAT:Page Attribute Table,页属性位,在页一级的粒度上设置内存属性。
G:Global,全局位。与TLB有关,为1表示该页是全局页,该页在高速缓存TLB中一直保存。
AVL:Available,可用位。为1表示用户进程可用该页,为0则不可用。对操作系统无效。

快表

分页机制实现了线性地址与物理地址的分离,它为内存管理提供了灵活性,但分页的数据结构毕竟都是存在于内存中的,相对于CPU来说内存时低速设备,因此分页无法解决I/O速度不匹配的问题。且多级页表(现代的64位操作系统已经实现了三级、四级页表的机制)的地址转换过程繁琐,涉及频繁的内存访问,这更拖慢了速度。
因为不管是几级页表,本质上都是虚拟地址向物理地址映射,因此需要有一个“捷径”来快速获得物理地址,免去中间的查表过程——快表(Translation Lookaside Buffer,TLB)应运而生。快表基于程序的局部性原理,将进来常用的地址和指令加载到一块专门的高速缓存中,缓存中存放的条目就是虚拟地址页到物理地址页的直接映射。具体来说,TLB中的条目就是虚拟地址的高20位到物理地址高20位的映射。除此之外TLB中还有一些属性位。
有了TLB,处理器在寻址之前就会用虚拟地址的高20位作为索引来查找TLB中的相关条目,如果匹配成功(或者成为命中)则返回虚拟地址所映射的物理页框地址,否则会走之前所属的查表流程,获得页框物理地址后再更新TLB。
本文部分内容摘自《操作系统真象还原》,有改动

你可能感兴趣的:(操作系统)