3.1.9 两级页表
单级页表存在的问题
问题一:页面需要很大连续的空间进行存储
某计算机系统按字节寻址,支持32位的逻辑地址,采用分页存储管理,页面大小为4KB,页表项长度为4B。
4KB=2^12B,因此页内地址要用12位表示,剩余的20位标识页号。
因此,该系统中用户进程最多有2^20页。相应的,一个进程的页表中,最多会有2^20=1048576个页表项,所以一个页表最大需要2^20*4B=2^22B,共需要2^22/2^12=2^10个页框存储该页表
根据页号查询页表的方法:
- K号页对应的页表项存放位置=页表始址+K*4
- 要在所有的页表项都连续存放的基础上才能使用这种方法找到页表项
- 需要专门给进程分配2^10=1024个连续的页框来存放它的页表
问题二:没必要让整个页表都常驻内存
根据局部性原理可知,很多时候,进程在一段时间内只需要访问某几个页面就可以正常运行了。因此没有必要让整个页表都常驻内存。
如何解决单级页表的问题?---问题一
问题一:页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。
如何解决进程在内存中必须连续存储的问题的?
- 将进程地址空间分页,并为其建立一张页表,记录各页面的存放位置
- 同样的思路可用于解决"页表必须连续存放"的问题,把必须连续存放的页表再分页
解决方案:
- 可将长长的页表进行分组,使每个内存块刚好可以放入一个分组
- 比如上个例子中,页面大小4KB,每个页表项4B,每个页面可存放1K个页表项,因此每1K个连续的页表项为一组,每组刚好占一个内存块,再将各组离散地存放到各个内存块中
- 另外,要为离散分配的页表再建立一张页表,称为页目录表,或称外层页表,或称顶层页表
两级页表的原理、地址结构
进程最多有2^20个页面
用20位二进制刚好可以表示0~2^20-1个页号
每个页面可存放4K/4=1K=2^10=1024个页表项
10位一级页号刚好可表示0~1023
如何实现地址变换
- 按照地址结构将逻辑地址拆分成三部分
- 从PCB中读出页目录表始址,再根据一级页号查页目录表,找到下一级页表在内存中的存放位置
- 根据二级页号查表,找到最终想访问的内存块号
- 结合页内偏移量得到物理地址
例:将逻辑地址(0000000000 0000000001 111111111111)转换为物理地址(用十进制表示)
如何解决单级页表的问题?---问题二
问题二:没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面
- 可以在需要访问页面时才把页面调入内存(虚拟存储技术),可以在页表项中增加一个标志位,用于表示该页面是否已经调入内存
- 若想访问的页面不在内存中,则产生的缺页中断(内中断),然后将目标页面从外存调入内存
1.若采用多级页表机制,则各级页表的大小不能超过一个页面
例:某系统按字节编址,采用40位逻辑地址,页面大小为4KB,页表项大小为4B,假设采用纯页式存储,则要采用( )级页表,页内偏移量为( )位?
- 页面大小=4KB=2^12B,按字节编址,因此页内偏移量为12位
- 页号=40-12=28位
- 页面大小=2^12B,页表项大小=4B,则每个页面可存放2^12/4=2^10个页表项
- 因此各级页表最多包含2^10个页表项,需要10位二进制位才能映射到2^10个页表项,因此每一级的页表对应页号应为10位。总共28位的页号至少要分为三级
如果只分为两级页表,则一级页号占18位,也就是说页目录表中最多可能有2^18个页表项,显然,一个页面是放不下这么多页表项的。
2.两级页表的访存次数分析(假设没有快表机构)
- 第一次访存:访问内存中的页目录表
- 第二次访存:访问内存中的二级页表
- 第三次访存:访问目标内存单元
32位线性逻辑地址的最高10位被硬件解释为页目录号P1,中间10位被硬件解释为页号P2,低12位解释为页内偏移
- 对于给定的逻辑地址A,由P1从页目录中找到页表分页所在的页框号
- 由P2,从页表分页中找到进程页所在的页框号
- 物理地址=页框号*页框大小+页内偏移
只是找的过程和一级的不同,计算的过程和一级的是一样的
- 两级页表结构中:页面大小为2^12=4KB
- 共有2^P1=2^10=1K个页表分页
- 每个页表分页含有2^P2=2^10=1K个页表项
两级页表的地址变换过程
两级页表的寻址:地址变换过程:
当进程切换时,欲运行的进程的页目录起始地址被写入页表寄存器。地址映射过程:
① 给定逻辑地址A,由硬件从中分离出页目录号p1、页号p2、页内偏移地址W。
- 页目录的p1号页表项的起始地址=页目录的起始地址(页表寄存器的值)+p1 ×页表项长度
从该地址处读取p1号所在的页表分页的页框号d1
② P1号页表分页中p2号页表项的起始地址=d1 ×页框大小+p2 ×页表项长度
从该地址处读取进程页所在的页框号d2
③ A对应的物理单元的地址=d2×页框大小+页内偏移地址W
总结
3.1.10 基本分段存储管理
什么是分段
进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名(在低级语言中,程序员使用段名来编程),每段从0开始编址
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻
分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。
段号的位数决定了每个进程最多可以分为几个段
段内地址位数决定了每个段的最大长度是多少
什么是段表
程序分为多个段,各段离散地装入内存,为了保证程序能正常运行,就必须能从物理内存中找到各个逻辑段的存放位置,为此,需为每个进程建立一张段映射表,简称"段表"
- 每个段对应一个段表项,其中记录了该段在内存中的起始位置(又称"基址")和段的长度
- 各个段表项的长度是相同的。例如:某系统按字节寻址,采用分段存储管理,逻辑地址结构为(段号16位,段内地址16位),因此用16位即可表示最大段长(段长16位表示)。物理内存大小为4GB(可用32位表示整个物理内存地址空间(基址32位表示))。因此,可用让段表项占16+32=48位,即6B。由于段表项长度相同,因此段号可以是隐含的,不占存储空间。若段表存放的起始地址为M,则K号段对应的段表项存放的地址为M+K*6
地址变换
分段、分页管理对比
(1)存储单位性质不同:
- 页是信息的物理单位。分页的主要目的是为了实现离散分配,提高内存利用率。分页仅仅是系统管理上的需要,完全是系统行为,对用户是不可见的。
- 段是信息的逻辑单位。分段的主要目的是更好地满足用户需求。一个段通常包含着一组属于一个逻辑模块的信息。分段对用户是可见的,用户编程时需要显式地给出段名。
- 分页是通过离散分配方式,提高内存利用率,是系统管理需要;
- 分段是用户编程模块化需要。
(2)存储大小不同:
- 页大小固定并且由系统决定
- 段的长度不固定,决定于用户编写的程序
(3)地址维度不同:
- 分页的用户进程地址空间是一维的,程序员只需要给出一个记忆符(逻辑地址)即可表示一个地址
- 分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名(代码段、数据段、堆栈段),也要给出段内地址
- 在分页系统中,虚拟地址是一个单一的长整数,通过位运算可以拆分为页号和页内偏移。这个过程是自动的,程序员只需要提供一个虚拟地址即可
- 在分段系统中,虚拟地址由两部分组成:段号和段内偏移。虽然逻辑地址可以拆分为段号和段内偏移,但段名在这里是必不可少的
段名的作用:
- 逻辑模块化:段名代表了程序的不同逻辑模块,如代码段、数据段、堆栈段等。每个段可以有不同的大小和访问权限。
- 编译和链接:在编译和链接阶段,编译器和链接器会生成不同的段名,程序员需要明确地指定这些段名来访问相应的数据。
- 内存保护:每个段可以有不同的访问权限(读、写、执行),通过段名可以更容易地实现内存保护。
(4)分段比分页更容易实现信息的共享和保护
- 不能被修改的代码称为纯代码或可重入代码(不属于临界资源),这样的代码是可以共享的。可修改的代码是不能共享的(比如,有一个代码段中有很多变量,各进程并发地同时访问可能造成数据不一致)
(5)分段比分页更容易实现信息的共享和保护
(6)访问一个逻辑地址需要几次访存?
- 分页(单级页表):第一次访存--查内存中的页表,第二次访存--访问目标内存单元。总共两次访存
- 分段:第一次访存--查内存中的段表,第二次访存--访问目标内存单元。总共两次访存
- 与分页系统类似,分段系统中也可以引入快表机构,将近期访问过的段表项放到快表中,这样可以少一次访问,加快地址变换速度。
总结
3.1.11 段页式管理方式
分页、分段的优缺点分析
段页式管理
将用户进程空间先划分成若干个段,每个段划分成若干个页,每个进程有一个段表,每个段 都有一个页表。段页式存储管理的逻辑地址形式由段号、段内页号和页内地址三部分组成。每一段表项存放某个段的页表起始地址和页表长度。
段页式管理的逻辑地址结构
- 段号的位数决定了每个进程最多可以分几个段
- 页号位数决定了每个段最大有多少页
- 页内偏移量决定了页面大小、内存块大小是多少
若系统是按字节寻址的,则段号占16位,因此该系统中,每个进程最多有2^16=64K个段
页号占4位,因此每个段最多有2^4=16页
页内偏移量占12位,因此每个页面=每个内存块大小=2^12B=4096B=4KB
"分段"对用户是可见的,程序员编程时需要显式地给出段号、段内地址。而将各段"分页"对用户是不可见的。系统会根据段内地址自动划分页号和页内偏移量。
因此段页式管理的地址结构是二维的
地址变换过程
段表和页表:
- 每个段对应一个段表项,每个段表项由段号、页表长度、页表存放块号(页表起始地址)组成。每个段表项长度相等,段号是隐含的。
- 每个页面对应一个页表项,每个页表项由页号、页面存放的内存块号组成。每个页表项长度相等,页号是隐含的
- 一个进程对应一个段表,每个段表项会对应一个页表,所以一个进程会对应多个页表
总结
3.2.1 虚拟内存的基本概念
传统存储管理方式的特征、缺点
虚拟内存的定义和特征
定义:
- 基于局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。
- 在程序执行过程中,当所访问的信息不在内存中时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。
- 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
- 在操作系统的管理下,在用户看来似乎有一个比实际内存大得多的内存,这就是虚拟内存
虚拟内存的三个主要特征:
- 离散性:采用离散方式进行内存空间的分配
- 多次性:无需在作业运行时一次性全部装入内存,而是运行分成多次调入内存
- 对换性:在作业运行时无需一直常驻内存,而是允许作业运行过程中,将作业换入、换出
- 虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量
如何实现虚拟内存技术
虚拟内存技术,允许一个作业分多次调入内存。如果采用连续分配方式,会不方便实现。因此,虚拟内存的实现需要建立在离散分配的内存管理方式基础上
主要区别:
- 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序(操作系统要提供请求调页(或请求调段)功能)
- 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存(操作系统要提供页面置换(或段置换)功能)
总结
3.2.2 请求分页管理方式
请求分页系统是在分页系统的基础上,增加了请求调页功能、页置换功能所形成的页式虚拟存储系统
请求分页存储管理与基本分页存储管理的主要区别:
- 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序(操作系统需要提供请求调页功能,将缺失页面从外存调入内存)
- 若内存空间不够,由操作系统负责将内存中用不到的页面换出外存(操作系统需要提供页面置换功能,将内存中暂时用不到的信息换出到外存)
页表机制
- 与基本分页管理相比,请求分页管理中,为了实现"请求调页",操作系统需要知道每个页面是否已经调入内存,如果还没调入,那么需要知道该页面在外存中存放的位置。
- 当内存空间不够时,要实现"页面置换",操作系统需要通过某些指标来决定到底换出哪个页面,有的页面没有被修改过,就不用再浪费时间写回外存,有的页面修改过,就需要将内粗中的旧数据覆盖,因此,操作系统也需要记录各个页面是否被修改的信息
缺页中断机构
假设此时要访问逻辑地址=(页号,页内偏移量)=(0,1024)
- 在请求分页系统中,每当要访问的页面不在内存时,便产生一个缺页中断,然后由操作系统的缺页中断处理程序处理中断
- 此时缺页的进程阻塞,放入阻塞队列,调页完成后再将其唤醒,放回就绪队列
- 如果此时内存中有空闲块,则为进程分配一个空闲块,将所缺页面装入该块,并修改页表中相应的页表项
- 如果内存中没有空闲块,则由页面置换算法选择一个页面淘汰,若该页面在内存期间被修改过,则要将其写回外存。未修改过的页面不用写回外存。
缺页中断:
- 缺页中断是因为当前执行的指令想要访问的目标页面未调入内存而产生的,因此属于内中断
- 一条指令在执行期间,可能产生多次缺页中断
- 如:copyA to B,即将逻辑地址A中的数据复制到逻辑地址B,而A,B属于不同的页面,则有可能产生两次中断
地址变换机构
请求分页存储管理与基本分页存储管理的主要区别:
- 在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序(操作系统要提供请求调页功能,将缺失页面从外存调入内存)
- 若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存(操作系统要提供页面置换的功能,将暂时用不到的页面换出到外存)
新增步骤:
- 新增步骤1:请求调页(查到页表项时进行判断)
- 新增步骤2:页面置换(需要调入页面,但没有空闲内存块时进行)
- 新增步骤3:需要修改请求页表中新增的表项
补充细节:
- 只有"写指令"才需要修改"修改位"。并且,一般来说只需修改快表中的数据,只有要将快表项删除时才需要写回内存中的慢表。这样可以减少访存次数。
- 和普通的中断处理一样,缺页中断依然需要保留CPU现场
- 需要某种"页面置换算法"来决定一个换出页面
- 换入/换出页面都需要启动慢速的I/O操作,可见,如果换入/换出太频繁,会有很大的开销
- 页面调入内存后,需要修改慢表,同时也需要将表项复制到快表中
在具有快表机构的请求分页系统中,访问一个逻辑地址时,若发生缺页,则地址的变换步骤是:
查快表(未命中)--查慢表(发现未调入内存)--调页(调入的页面对应的表项会直接加入快表)--查快表(命中)--访问目标内存单元
总结
3.2.3 页面置换算法
请求分页中,若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存(操作系统要提供页面置换的功能,将暂时用不到的页面换出到外存)
- 需要使用页面置换算法决定应该换出哪个页面
最佳置换算法(OPT)
最优置换算法(OPT,Optimal):每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率
实现方法:
- 无法实现,理想化的方法,无法提前预知后面会使用哪个页面
做法:
- 看后面的序列,内存中现存的页面在后面再也不出现,或者最晚出现,选择淘汰这个页面
- 如上面内存中存在的页面是7,0,1,需要调入2,而且没有空闲的内存块,需要从7,0,1中选择一个换出,看以看到0304230321201701,7号页面是最晚出现的,所以把7号换出到外存
整个过程缺页中断发生了9次,页面置换发生了6次
注意:
- 缺页时未必发生页面置换。若还有可用的空闲内存块,就不用进行页面置换
缺页率:9/20=45%
最优置换算法可以保证最低的缺页率,但实际上,只有在进程执行的过程中才能知道接下来会访问到的是哪个页面。操作系统无法提前预判页面访问序列。因此,最佳置换算法是无法实现的,是一种理想化的算法。
先进先出置换算法(FIFO)
先进先出置换算法(FIFO):每次选择淘汰的页面是最早进入内存的页面
实现方法:
- 把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。队列的最大长度取决于(等于)系统为进程分配了多少个内存块。
例1:假设系统为某进程分配了三个内存块,并考虑到有以下页面引用串:
分配三个内存块:共发生了9次缺页
例2:假设系统为某进程分配了四个内存块,并考虑到有以下页面号引用串:
分配四个内存块:共发生了10次缺页
Belady异常--当进程分配的物理块数增多时,缺页次数不减反增的异常现象
只有FIFO算法会产生Belady异常。另外,FIFO算法虽然实现简单,但是该算法与进程实际运行时的规律不适应,因为先进入的页面也有可能最经常被访问,因此,算法性能差。
最近最久未使用置换算法(LRU)
最近最久未使用置换算法(LRU,least recently used):每次淘汰的页面是最近最近未使用的页面
实现方法:
- 赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t
- 当需要淘汰一个页面时,选择现有页面中(内存块中)t值最大的,即最近最久未使用的页面
- 在手动做题时,若需要淘汰页面,可以逆向检查此时在内存中的几个页面号。
- 在逆向扫描过程中最后一个出现的页面就是要淘汰的页面
该算法的实现需要专门的硬件支持,虽然算法性能好,但是实现困难,开销大
时钟置换算法(CLOCK)--综合看最好
- 最佳置换算法性能好,但无法实现
- 先进先出算法实现简单,但算法性能差
- 最近最久未使用算法性能好,是最接近OPT算法性能的,但是实现起来需要专门的硬件支持,算法开销大
时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未使用算法(NRU,Not Recently Used)
简单的CLOCK算法实现方法:
- 为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需要检查页的访问位
- 如果是0,就选择该页换出
- 如果是1,则将它置为0,暂不换出,继续检查下一个页面
- 若第一轮扫描中所有页面都是1,则将这些页面的访问为依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK算法选择一个淘汰页面最多会经过两轮扫描)
例:假设系统为某进程分配了五个内存块,并考虑到有以下页面号引用串:
1,3,4,2,5,6,3,4,7
前五个顺利放入
到6的时候,经过一轮扫描,到1,6装到1以前占有的地方
到3的时候,相当于是再次访问到了3,3已经是在内存块里面了,把3的访问位从0变为1即可,
指针位置不变
到4的时候和3一样,4已经是在内存块里面了,直接把它的访问位变为1即可
指针位置不变
到7的时候,指针从上次扫描到的位置3开始,遇到第一个访问位已经是0的2,将其换出去
改进型的时钟置换算法
只需要一轮扫描
队头开始
开始状态
第一轮
需要两轮扫描
第一轮(没有找到)
第二轮(修改访问位,找(0,1))
需要三轮扫描
第一轮,没找到
第二轮(找(0,1)),没找到
第三轮(找(0,0))
需要四轮扫描
第一轮
第二轮(找(0,1))
第三轮(找(0,0))
第四轮(找(0,1))
总结
3.2.4 页面分配策略、抖动、工作集
页面分配、置换策略
驻留集
固定分配--局部置换
可变分配--全局置换
可变分配--局部置换 
何时调入页面
从何处调入页面
抖动(颠簸)现象
工作集
总结
3.2.5 内存映射文件
内存映射文件
传统的文件访问方式