连续分配存储方式,是将进程存放在一个连续的内存空间中,从而容易造成碎片问题。那么有什么办法可以解决碎片问题呢?那我们不要将进程存在一个连续的空间就好了。于是就产生了将一个进程分散的分配到许多不相邻的分区中,就可以解决这一问题。
页式存储管理的基本思想
将内存空间分成大小相同的存储块,并按顺序编号(一般从0开始)。相应的将进程的逻辑地址空间分成若干个与内存块大小相等的块,但是为了方便区分,我们称为页。(也就是说,在实际的内存中我们称为块,在逻辑地址中我们称为页)。在为进程分配内存空间的时候,以页为单位进行。进程中若干个页分别装入多个不相邻的存储块中,通常,进程的最后一页通常装不满一个存储块,形成不可利用的碎片,称为页内碎片。(所以怎么分页,页的大小多少为好都要考虑,这里不多说)教材的图很易懂的描述了这一过程:
图F中的11就是装载完毕后的页内碎片。
页表
为了找到每个页所对应的存储块,系统为每个进程建立一张索引表,其中的每一项称为页表项,里面记录了相应页在内存中对应的物理块号。
进程在执行时,通过查找页表,从而找到对应的物理块号,因此,页表的作用就是实现从页号到块号的地址映射。
此外,操作系统还为每个进程建立了空闲块号,并从小到大进行排序。用来记录尚未分配的存储块的块号。
页式存储的地址变换(重点)
地址变换机构的任务,就是实现逻辑地址到物理地址之间的动态重定位。
因为进程的执行要装入内存,所以为了更方便快速的的找到对应的进程,页表绝大多数情况下是驻留在内存中的。因此,操作系统设置一个页表寄存器,用来存放内存的起始地址跟页表的长度。
当进程中的某个逻辑地址,被访问执行的时候,硬件逻辑地址变换结构会根据页的大小自动将有效的逻辑地址分成页号和页内偏移两部分:
变换过程如下:
- 首先,页号与页表寄存器中的页表长度进行比较,若页号大于页表长度,则产生越界中断。(判越界)
- 否则,通过页表起始地址,找出页表。
- 根据页号,找出相应的页表项,得到该页的存储块号
- 根据块号与页内偏移,算出实际的物理地址。
计算公式如下:
(这部分内容是核心重点,打算先介绍公式,在刷题篇里面详细做这一类题)。
刚刚我们讲到,页表通常是存在内存当中的所以CPU每存取一个数据的时候都需要访问两次内存。第一次是访问内存中的页表,从而找到该页的存储块号,第二次才是真正的访问物理地址。
页表的组织
由前面可以知道,页式存储结构的地址结构为页号+页内移位。若32位的逻辑地址空间按照下面的方式进行分配:
那么各代表的意思为:
0—11为页内地址,表示每一页的大小(按字节编址,那么上面的表示的就是2的11次方B,也就是4kb空间)
11-31为页号,一个页号对应一个页表项。所以地址空间最多可以有2的20次页。也就是1MB。又因为一个页表项为4个字节(32/8=4),所以每个进程页表就要占用4MB内存,还要求空间连续,显然不实际(一个进程光是页表就那么大,其他数据加起来内存肯定不够用啊)。那么怎么解决呢?
- 在内存上:如果我们将一部分的页表项调入内存,其余的仍驻留在磁盘,需要的时候再调入(这就是虚拟存储管理的基本思想)
- 从空间上:采用离散方式将页表放在多个不连续的内存空间中。(这个方式我们刚刚才用到)
所以,我们可以再采用一次分页的思想。也就是所谓的二级分页。
二级分页
对于难以找到连续的内存空间存放页表的问题,可将页表分页。按之前的思路,将页表依次编号。这样就可以将每一页都分别放在一个不同的存储体中。为了记录这些页在内存中的位置和存放情况,我们同样为离散分配的页表再建立一张页表,称之为外层页表。就像这样:
例如,逻辑地址空间同样是32位,显然,每一页的大小为4KB。
如果采用的是一级页表,之前算过,光页表项就有1MB个
如果采用的是二级页表,那么我们对页表再分页,这个时候,外层页表的地址位数表示的是每页有多少个页表项,内层页号的地址数表示的就是有多少个这样的页表
实际的原理跟之前的分页是一样的。(不过这里容易搞混,建议多做题)
搞定了离散存放的问题,就是内存问题了。实际的分配时,通常将外层页表放在内存中,根据需要再将存在辅存中的内层页号的一页或者几页调入内存,为了表明哪些页表在内存中,可在外层页表中增加一状态位。当然每当访问不在内存中的页表的时候,就会产生缺页中断。