第三章 存储器管理
3.1 内存的基础知识
3.1.1 内存的定义
内存是用于存放数据的硬件。
3.1.2 内存的作用
内存中有存储单元,程序执行前需要先放到内存中才能被CPU处理。
按字节编址,每个存储单元大小为1字节,每个字节位8个二进制位
按字编址,每个存储单元大小为1个字,每个字的大小为16个二进制位
3.2 进程允许的基本原理
3.2.1 指令的工作原理
指令:能被CPU识别的命令
3.2.2 逻辑地址vs物理地址
逻辑地址:相对地址
物理地址:实际放入内存中的起始位置,绝对地址
3.2.3 从写程序到程序运行
编译:由编译程序将用户源代码编译成若干个目标模块
链接:由链接程序将编译后形成的一组目标模块以及所需库函数链接在一起形成一个完整的装入模块
装入(装载):由装入程序将装入模块装入内存运行
3.2.4 装入的三种方式
绝对装入
在编译时,如果知道程序将放到内存中的哪个位置,编译程序将产生绝对地址的目标代码。
绝对装入值适用于单道程序环境
静态重定位
又称可重定位装入。编译、链接后的装入模块的地址都是从0开始的,指令中使用的地址、数据存放的地址都是相对于起始地址而言的逻辑地址。可根据内存的当前情况,将装入模块装入到内存的适当位置。装入时对地址进行重定位,将逻辑地址变为物理地址。
特点:在一个作业装入内存时,必须分配其要求的全部内存空间。如果没有足够的内存,就不能装入该作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。
动态重定位
又称动态运行时装入。编译、链接后的装入模块的地址都是从0开始的。装入程序包装入模块装入内存后,并不会立即把逻辑地址转换为物理地址,而是把地址转换推迟到程序真正要执行时才进行,需要一个重定位寄存器的支持。
3.2.5 链接的三种方式
静态链接
再程序运行之前,先将各目标模块及它们所需的库函数连接成一个完整的可执行,之后不再拆开。
载入时动态链接
将各目标模块装入内存时,边装入边链接的链接方式
运行时动态链接
在程序执行中需要该目标模块时,才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。
3.3 覆盖于交换
3.3.1 覆盖技术
覆盖技术的思想:将程序分为多个段(多个模块)。
内存中分为一个固定区和若干个覆盖区。
需要常驻内存的段放在固定区中,调入后就不再调出。
不常用的段放在覆盖区,需要用到时调入内存,用不到时调出内存。
必须由程序员声明覆盖结构,操作系统完成自动覆盖。
缺点:对用户不透明,增加了用户编程负担。
3.3.2 交换技术
交换技术的设计思想:内存空间紧张时,系统将内存中某些进程暂时换出外存,把外存中某些已具备运行条件的进程换入内存(进程在内存与磁盘间动态调度)
中级调度:就是要决定将哪个处于挂起状态的进程重新调入内存。
暂时换出外存等待的进程状态为挂起状态,挂起状态又可以进一步细分为就绪挂起、阻塞挂起两个状态
3.4 连续分配管理方式
3.4.1 单一连续分配
在单一连续分配方式中,内存被分为系统区和用户区。
系统区通常位于内存的低地址部分,用于存放操作系统相关数据
用户区用于存放用户进程相关数据
内存中只能由一道用户程序,用户程序独占整个用户区空间。
优点
实现简单;无外部部署;可以采用覆盖技术扩充内存;不一定需要采取内存保护
缺点
只能用于单用户、单任务的操作系统中;由内部碎片;存储器利用率低
3.4.2 固定分区分配
为了能在内存中装入多道程序,且这些程序之间又不会相互干扰,于是将整个用户空间划分为若干个固定大小的分区,在每个分区中只装入一道作业,这样就形成了最早的、最简单的一组可运行多道程序的内存管理方式。
分区大小相等:缺乏灵活性,但是很适合用于用一台计算机控制多个相同对象的场合
分区大小不等:增加了灵活性,可以满足不同大小的进程需求,根据常在系统中的作业大小情况进行划分
优点:实现简单、无外部碎片
缺点:a、当用户程序太大时,可能所有对方分区都不能满足需求,此时不得不采用覆盖技术来解决,但这会降低性能;b、会产生内部碎片,内存利用率低。
3.4.3 动态分区分配
又称可变分区分配,这种分配方式不会预先划分内存分区,而是在进程装入内存时,根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。
常用的数据结构
1、空闲分区表:每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息
2、空闲分区链:每个分区的起始部分和末尾部分分别设置前向指针和后向指针。起始部分处还可记录分区大小等信息
动态分区分配没有内部碎片,但有外部碎片。
内部碎片:分配给某进程的内存区域中,如果有些部分没有用上
外部碎片:指内存中的某些空闲分区由于太小而难以利用
回收内存分区时的四种情况
1、回收区之后有相邻的空闲分区,那么两个部分的相邻的空闲分区合并为一个
2、回收区之前有相邻的空闲分区,那么两个部分的相邻的空闲分区合并为一个
3、回收区前、后都有相邻的空闲分区,那么三个部分的相邻的空闲分区合并为一个
4、回收区前、后都没有相邻的空闲分区,那么就新增一个表项
3.5 动态分区分配算法
3.5.1 首次适应算法(First Fit)
算法思想:每次都从低地址开始查找,找到第一个能满足大小的空闲分区
实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区
3.5.2 最佳适应算法(Best Fit)
算法思想:由于动态分区分配时一种连续分配方式,为各进程分配的空间必须时连续的一整片区域。为了保证当大进程到来时有连续的大片空间,可以尽可能多地留下大片的空闲区,即先使用更小的空闲区
实现:空间分区按容量递增次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区
缺点:每次都选最小的分区进行分配,会留下越来越多的、很小的、难以利用的内存块。因此这种方法会产生很多的外部碎片。
3.5.3 最坏适应算法(Worst Fit)
又称最大适应算法(Largest Fit)
算法思想:为了解决最佳适应算法的问题——即留下太多难以利用的小碎片,可以在每次分配时优先使用最大的连续空闲区,这样分配后剩余的空闲区就不会太小,更方便使用。
实现:空闲分区按容量递减次序链接。每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区
缺点:每次都选最大的分区进行分配,虽然可以让分配留下的空闲区更大,更可用,但是这种方式会导致较大的连续空闲区被迅速用完。如果之后有大进程到达,就没有内存分区可用了。
3.5.4 邻近适应算法(Next Fit)
算法思想:首次适应算法每次都从链头开始查找的。这可能会导致低地址部分出现很多小的空闲分区,而每次分配查找时,都要经过这些分区,因此也增加了查找的开销。如果每次都从上次擦或者结束的位置开始检索,就能解决上述问题。
实现:空闲分区以地址递增的顺序排列。每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求的第一个空闲分区。
缺点:会使高地址的大分区也被用完。
3.6 基本分页存储管理的基本概念
连续分配:为用户进程分配的必须是一个连续的内存空间
非连续分配:为用户进程分配的可以是一些分散的内存空间
基本分页存储管理的思想:把内存分为一个个相等的小分区,再按照分区大小把进程拆分成一个个小部分。
页框(页帧、内存块、物理块):将内存空间分为一个个大小相等的分区,每个分区就是一个页框
页框号:每个页框有一个编号
页(页面):将用户进程的地址空间也分为与页框大小相等的一个个区域
如何地址转换
1、计算出逻辑地址对应的页号
2、找到对应页面再内存中的存放位置
3、算出逻辑地址对应的页内偏移量
4、物理地址=页面地址+页内偏移量
页号、页内偏移量的计算
1、页号=逻辑地址/页面大小;
2、业内偏移量=逻辑地址%页面大小;
或根据逻辑地址结构计算,逻辑地址=[页号P,页面偏移量W]
页表
1、页表记录进程页面和实际存放的内存块之间的对应关系
2、一个进程对应一张页表,进程的每一页对应一个页表项,每个页表由“页号”和“块号”组成
3、每个页表项的长度是相同的,页号是“隐含”的
3.7 基本地址变换机构
基本地址变换机构可以借助进程的页表将逻辑地址转换为物理地址
通常会在系统中设置一个页表寄存器(PTR),存放页表在内存中的起始地址F和页表长度M。
进程未执行时,页表的始址和页表长度放在进程控制块(PCB)中,当进程被调度时,操作系统内核会把它们放在页表寄存器中。
页表寄存器的作用:存放页表起始地址、存放页表长度
地址变换过程
1、根据逻辑地址算出页号、页内偏移量
2、页号的合法性检查(与页表长度对比)
3、若页号合法,再根据页表起始地址、页号找到对应页表项
4、根据页表项中记录的内存块号、页内偏移量得到最终的物理地址
5、访问物理内存对应的内存单元
其他
1、页内偏移量位数与页面大小之间的关系(要能用其中一个条件推出另一个条件)
2、页式管理中地址式一维的
3、实际应用中,通常使一个页框恰好能放入整数个页表项
4、为了方便找到页表项,页表一般是放在连续的内存块中的
3.8 具有快表的地址变换机构
3.8.1 局部性原理
时间局限性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据很有可能再次被访问
空间局限性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能被访问。
3.8.2 快表(TLB)
又称联想寄存器(TLB),是一种访问速度比内存快很多的高速缓冲存储器,用来存放当前访问的若干页表项,以加速地址变换的过程。
3.8.3 地址的变换过程
3.9 两级页表
3.9.1 单级页表存在的问题
1、所有页表项必须连续存放,页表过大时需要很大的连续空间
2、在一段时间内并非所有页面都用得到,因此没必要让整个页表常驻内存
3.9.2 两级页表
1、将长长的页表再分页
2、逻辑地址结构:要根据逻辑地址位数、页面大小、页表项大小确定多级页表的逻辑地址结构
3.9.3 地址转换
1、按照地址结构将逻辑地址拆分成三部分
2、从PCB中读出页目录表始址,根据一级页号查页目录表,找到下一级页表在内存中的存放位置
3、根据二级页号查表,找到最终想访问的内存块号
4、结合页内偏移量得到物理地址
多级页表中,各级页表的大小不能超过一个页面。若两级页表不够,可以分更多级
多级页表的访问次数(假设没有快表机构)——N级页表访问一个逻辑地址需要N+1次访存
3.10 基本分段存储管理
3.10.1 分段
进程的地址空间:按照程序自身的逻辑关系划分为若干个段,每个段都有一个段名,每段从0开始编址。
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻
段号的位数决定了每个进程最多可以分几个段
段内地址位数决定了每个段的最大长度是多少
3.10.2 段表
记录逻辑段到实际存储地址的映射关系
每个段对于一个段表项。各段表项长度相同,由段号(隐含)、段长、基址组成
3.10.3 地址变换
1、由逻辑地址得到段号、段内地址
2、段号与段表寄存器中的段长度比较,检查是否越界
3、由段表始址、段号找到对应段表项
4、根据段表中的记录的段长,检查段内地址是否越界
5、由段表中的“基址+段内地址”得到最终的物理地址
6、访问目标单元
3.10.4 分段vs分页
1、分页对用户不可见,分段对用户可见
2、分页的地址空间是一维的,分段的地址空间是二维的
3、分段更容易实现信息的共享和保护
4、分页(单级页表)、分段访问一个逻辑地址都需要两次访存,分段存储中也可以引入快表机构
3.11 段页式管理方式
3.11.1 分页、分段的优缺点
3.11.2 段页式管理
1、将地址空间按照程序自身的逻辑关系划分为若干分段,在将各段分为大小相等的页面
2、将内存空间分为与页面大小相等的一个个内存块,系统以块为单位为进程分配内存
3、逻辑地址结构:(段号、页号、页内偏移量)
3.11.3 段表、页表
每个段对应一个段表项。各段表项长度相同,由段号(隐含)、页表长度、页表存放地址组成
每个页对应一个页表项。各页表项长度相同,由段号(隐含)、页面存放的内存块号组成
3.11.4 地址转换
1、由逻辑地址得到段号、页号、页内偏移量
2、段号与段表寄存器中的段长度比较,检查是否越界
3、由段表始址、段号找到对应段表项
4、根据段表中记录的页表长度,检查页号是否越界
5、由段表中的页表地址、页号得到查询页表,找到相应页表项
6、由页面存放的内存块号、页内偏移量得到最终的物理地址
7、访问目标单元
访问一个逻辑地址所需访存次数:
1、第一次——查段表、第二次——查页表、第三次——访问目标单元
2、可引入快表机构,以段号和页号为关键字查询快表,即可直接找到最终的目标页面存放位置。引入快表后仅需一次访存
3.12 虚拟内存的基本概念
3.12.1 传统存储管理方式的特征、缺点
连续分配:单一连续分配、固定分区分配、动态分区分配
非连续分配:基本分页存储管理、基本分段存储管理、基本段页式存储管理
一次性:作业必须一次性全部装入内存后才能开始运行。
驻留性:一旦作业被装入内存,就会一直驻留在内存中,直至作业运行结束。事实上,在一个时间段内,只需要访问作业的一小部分数据即可正常运行,这就导致了内存中会驻留大量的、暂时用不到的数据,浪费了宝贵的内存资源。
3.12.2 局部性原理
时间局部性:如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行;如果某个数据被访问过,不久之后该数据可能再次被访问。
空间局限性:一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也有可能被访问。
3.12.3 虚拟内存的定义和特征
程序不需全部装入即可运行,运行时根据需要动态调入数据,若内存不够,还需换出一些数据
特征
1、多次性:无需在作业运行时一次性全部装入内存,而是允许被分成多次调入内存
2、无需在作业运行时一直常驻内存,而是允许在作业运行过程中,将作业换入、换出
3、虚拟性:从逻辑上扩充了内存的容量,使用户看到的内存容量,远大于实际的容量
3.12.4 虚拟内存技术的实现
访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存(请求调页功能)
内存空间不够时,将内存中暂时用不到的信息换出到外存(页面置换功能)
实现:请求分页存储管理、请求分段存储管理、请求段页式存储管理
3.13 请求分页管理方式
3.13.1 页表机制
在基本分页的基础上增加了几个表项
1、状态位:表示页面是否已在内存中
2、访问字段:记录最近被访问过几次,或记录上次访问的时间,供置换算法选择换出页面时参考
3、修改位:表示页面调入内存后是否被修改过,只有修改过的页面才需在置换时写回外存
4、外存地址:页面在外存中存放的位置
3.13.2 缺页中断机构
1、找到页表项后检查页面是否已在内存,若没在内存,产生缺页中断
2、缺页中断处理中,需要将目标页面调入内存,有必要时还要换出页面
3、缺页中断属于内中断,属于内中断中的故障,即可能被系统修复的异常
4、一条指令在执行过程中可能产生多次缺页中断
3.13.3 地址变换机构
1、找到页表项是需要检查页面是否在内存中
2、若页面不再内存中,需要请求调页
3、若内存空间不够,还需换出页面
4、页面调入内存后,需要修改相应页表项
3.14 页面置换算法
3.14.1 最佳置换算法
思想:每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。
最佳置换算法可以保证最低的缺页率。但实际上,只有在进程执行的过程中才能知道接下来会访问到的是哪个页面。操作系统无法提取预判页面访问序列。
3.14.2 先进先出置换算法
先进先出置换算法:每次选择淘汰的页面是最早进入内存的页面
实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择对头页面即可。队列的最大长度取决于系统为进程分配了多少个内存块。
Belady异常——当为进程分配的物理块数增大时,缺页次数不减反增的异常现象
3.14.3 最近最久未使用置换算法
LRU:每次淘汰的页面是最近最久未使用的页面
实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的实际t。当需要淘汰一个页面时,选择现有页面中t值最大的,即最近最久未使用的页面
该算法的实现需要专门的硬件支持,虽然算法性能好,但是实现困难,开销大。
3.14.4 时钟置换算法
3.14.5 改进型的时钟置换算法
3.15 页面分配策略
3.15.1 驻留集
驻留集:请求分页存储管理中给进程分配的物理块的集合
固定分配:操作系统为每个进程分配一组固定数目的物理块,在进程运行期间不做任何变化。
可变分配:先为每个进程分配一定数目的物理块,在进程运行期间,可根据情况做适当的增加或减少。即驻留集大小可变
局部置换:发生缺页时只能选进程自己的物理块进行置换
全局置换:可以将操作系统保留的空闲物理块分配给缺页进程,也可以将别的进程持有的物理块置换到外存,再分配给缺页进程。
3.15.2 页面分配、置换策略
固定分配局部置换:进程运行前就分配一定数量物理块,缺页时只能换出进程自己的某一页
可变分配全局置换:只要缺页就分配新物理块,可能来自空闲物理块,也可能需换出别的进程页面
可变分配局部置换:频繁缺页的进程,多分配一些物理块;缺页率很低的进程,回收一些物理块,直到缺页率合适
3.15.3 调入页面的时机
1、预调页策略:一般用于进程运行前
2、请求调页策略:进程运行时,发现缺页再调页
3.15.4 从何处调页面
1、对换区——采用连续存储方式,速度更快;文件区——采用离散存储方式,速度更慢
2、对换区足够大:运行将数据从文件区复制到对换区,之后所有的页面调入、调出都是再内存与对换区之间进行
3、对换区不够大:不会修改的数据每次都从文件区调入;会修改的数据调出到对换区,需要时再从对换区调入
4、UNIX方式:第一次使用的页面都从文件区调入;调出的页面都写回对换区,再次使用时从对换区调入
3.15.5 抖动现象
刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动。
产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数。
3.15.6 工作集
在某段时间间隔里,进程实际访问页面的集合。驻留集大小一般小于工作集大小
用于进程运行前
2、请求调页策略:进程运行时,发现缺页再调页
3.15.4 从何处调页面
1、对换区——采用连续存储方式,速度更快;文件区——采用离散存储方式,速度更慢
2、对换区足够大:运行将数据从文件区复制到对换区,之后所有的页面调入、调出都是再内存与对换区之间进行
3、对换区不够大:不会修改的数据每次都从文件区调入;会修改的数据调出到对换区,需要时再从对换区调入
4、UNIX方式:第一次使用的页面都从文件区调入;调出的页面都写回对换区,再次使用时从对换区调入
3.15.5 抖动现象
刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动。
产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数。
3.15.6 工作集
在某段时间间隔里,进程实际访问页面的集合。驻留集大小一般小于工作集大小