第四章-存储器管理
4.1 存储器的层次结构
对于通用计算机而言,存储层次至少分为三级:最高层为CPU寄存器,中间为主存,最低层是辅存,速度逐级变慢,容量逐级增大。
4.2 程序的装入和链接
4.2.1 程序的装入
- 装入:由装入程序将装入模块装入内存运行。
- 链接:由链接程序将编译后形成的一组目标模块,以及所需库函数链接在一起,形成一个完整的装入模块。
- 重定位(修改程序中的相对地址)
- 编译时重定位的程序只能放在内存固定位置
- 载入时重定位(静态重定位装入)的程序一旦载入内存就不能动了
- 重定位最合适的时机:运行时重定位(动态重定位装入)
-
绝对装入方式
逻辑地址 = 实际内存中的地址(适用于单道程序环境)
-
可重定位装入方式
数据地址和指令地址都需要修改
静态重定位:地址变换在装入时一次完成
-
动态运行时装入方式
把地址转换推迟到程序真正要执行时进行,装入后的地址仍是相对地址
4.2.2 程序的链接
- 静态的链接方式
- 装入时动态链接
- 运行时动态链接(在执行过程中把需要调入的模块才装入内存可以增快速度,节省大量内存)
4.3 连续分配存储管理方式
内存分配的过程中会产生一些内存碎片,即空闲内存不能被利用,先了解一下内存碎片的概念:
- 外部碎片: 在分配单元间的未使用内存。
- 内部碎片: 在分配单元中的未使用内存,取决于分配单元大小是否要取整。
地址空间以及地址的生成:
- 物理地址空间:硬件支持的地址空间(主存和磁盘)。
- 逻辑地址空间:一个运行的程序所拥有的内存范围,在CPU运行的进程看到的地址。
逻辑地址与物理地址的转换:
- ALU需要某个逻辑地址的内存的内容。
- 内存管理单元(MMU)寻找在逻辑地址和物理地址之间的映射;如果没有就从内存中找。
- 控制器从总线发送在物理的内存内容的请求。
- 内存发送物理地址内存给CPU 。
第二步映射的建立和查找由OS完成。
4.3.1 单一连续分配方式
把内存分为系统区和用户区两部分,系统区仅提供给OS使用,通常放在内存低址部分,用户区是指除系统区以外的全部内存空间,提供给用户使用。(用户区只运行一道程序)
用于单用户、单任务的操作系统中。
4.3.2 固定分区分配
将内存用户空间划分为若干个固定大小的区域,在每个分区中只装入一道作业,便可以有多道作业并发执行。
当有一空闲分区时,便可以再从外存的后备作业队列中,选择一个适当大小的作业装入该分区,当该作业结束时,可再从后备作业队列中找出另一作业调入该分区。
实现:
为便于内存分配,通常将分区按大小进行排队,并为之建立一张分区使用表,其中包括每个分区的起始地址、大小及状态(是否已分配)。
当有一用户程序要装入时,由内存分配程序检索该表,从中找出一个能满足要求的、尚未分配的分区,将之分配给该程序,然后将该表项中的状态置为“已分配”;若未找到大小足够的分区,则拒绝为该用户程序分配内存。
4.3.3 动态分区分配
1、概念
又称为可变分区分配,根据进程的实际需要,动态的位置分配内存空间。当程序被加载执行时,分配一个进程指定大小可变的分区(块、内存块)分区的地址是连续的。
2、数据结构
描述空闲分区和已分配分区的情况,常用空闲分区表和空闲分区链两种形式。
3、最先匹配(First Fit Allocation)策略
思路:
分配n个字节,使用第一个可用的空间比n大的空闲块。
实现:
将空闲分区列表按照地址顺序排序,分配过程时,搜索一个合适的分区,放入第一个合适的分区,释放分区时,检查是否可与临近的空闲分区合并。
特点:
- 优点:简单,在高地址空间有大块的空闲分区
- 缺点:外部碎片多,分配大块时较慢
4、最佳匹配(Best Fit Allocation)策略
思路:
分配n字节分区时, 查找并使用不小于n的最小空闲分区。
实现:
空闲分区列表按照大小排序,分配时,查找一个合适的分区,释放时,查找并且合并临近的空闲分区(如果找到)。
特点:
- 优点:大部分分配的尺寸较小时,效果很好
- 缺点:外部碎片,释放分区较慢,容易产生很多无用的小碎片
5、最差匹配(Worst Fit Allocation)策略
思路:
分配n字节,使用尺寸不小于n的最大空闲分区
实现:
空闲分区列表按由大到小排序,分配时,选最大的分区,释放时,检查是否可与临近的空闲分区合并,进行可能的合并,并调整空闲分区列表顺序
特点:
- 优点:中等大小的分配较多时,效果最好,避免出现太多的小碎片
- 缺点:释放分区较慢,外部碎片,容易破坏大的空闲分区,因此后续难以分配大的分区
6、碎片整理
紧凑(compaction):通过移动分配给进程的内存分区,以合并外部碎片,要求所有的应用程序可动态重定位。
分区对换(Swapping in/out): 通过抢占并回收处于等待状态进程的分区,以增大可用内存空间(挂起)。
4.3.4 基于顺序搜索的动态分区算法
分区分配内存管理的主要保护措施是界地址保护
4.3.5 基于索引搜索的动态分区算法
-
快速适应算法
将空闲分区根据进程常用容量大小进行分类,单独设立一个空闲分区链表。
在内存中设立一张管理索引表
-
伙伴系统所有的空闲页面分为10个快链表
-
哈希算法
构建一个以空闲分区大小为关键字的哈希表
每一个表象记录了一个对应的空闲分区链表表头指针
4.3.6 可重定位分区分配
4.4 对换
4.5 分页存储管理方式
分区分配的不足:会产生很多碎片,要求一段较大并且连续的空间
解决:把作业连续离散分配到内存
4.5.1 页面与页表
1、页面和物理块
①页面:将一个进程的逻辑地址空间分为若干个固定大小相等的片,称为“页”
②(物理)块:也将内存空间分成若干个大小相等的片
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1eCQra6-1640852407905)(第四章-存储器管理.assets/image-20211107203059756.png)]
页面大小:页面过小,虽然可以减少内存碎片,提高内存利用率,但是会导致单个进程占用较多页面,导致进程页表过长,占用内存,降低页面换进换出的效率;页面过大虽然提高了换进换出的效率,但是碎片大,一遍页面大小是2的幂,通常为1KB – 8KB。
2、地址结构
-
逻辑地址的表示:页号P+偏移量W,即页内地址。
如图所示,0-11位为页内地址,说明每页大小为4KB,12-31位为页号,说明最多允许有1M页。
3、页表
4.5.2 地址变换机构
- 将逻辑地址的页号转换为内存中的物理块号,需要借助页表完成
- 页内地址和物理块内地址不需转换
物理地址 = 块号 * 页面大小 + 页内地址
1、基本的地址变换机构
如图所示,页表寄存器存放页表始地址和页表长度。指令给出逻辑地址后,先判断页号是否大于等于页表长度,如果是,则说明越界了,直接中断。否则,与页表始地址相加得到页号,然后在页表中找到对应的物理块号,根据页内地址直接找到需要的数据。
TODO:例题见PPT
2、具有快表的地址变换机构
页表存放在内存中,使得CPU每次读取数据都要进行两次访问,为了提高速度,利用局部性原理,在寄存器中设置一张块表(TLB),先在快表中找,若未命中则去页表中查找,原理类似Cache。
4.5.4 两级和多级页表
现代计算机的逻辑地址空间非常大,这样的环境下使得页表必须非常大,每个进程的页表就要占用大量的空间,且还是连续的空间,不太现实。
针对难以找到大的连续的内存空间存放页表的问题,可以将页表进行分页,形成二级页表,使得每个页面的大小与内存物理块大小相同,将其编号,然后离散地将各个页面存放在不同的物理块中,同时也要为离散后的页表再建立一张页表称为外层页表,记录页表页面的物理块号。
同样外表也需要一个外层页表寄存器用于存放外表的始址,逻辑地址结构为:外层页号+外层页内地址+页内地址
4.6 分段存储管理方式
4.6.1 分段
进程的段地址空间被分成若干段,每个段定于了一组逻辑信息,如:主代码段、子模块代码段、公用库代码段、堆栈段(stack)、堆数据(heap)、初始化数据段、符号表等。段表示访问方式和存储数据等属性相同的一段地址空间。对应一个连续的内存“块”,若干个段组成进程逻辑地址空间。
将每个段进行编号,称为段号,每个段从0开始编址,采用一段连续的地址空间,每段长度可以不相等,由逻辑信息组的长度决定。其逻辑地址由段号+段内地址组成。
4.6.2 段表
进程中的各个段可以离散地放入内存,为了找到逻辑地址对应的内存中的物理地址,需要一张映射表,称为段表,每个段占一个表项,记录在内存中的起始位置(基址)和段的长度。段表放在寄存器中可以提高地址转换速度,但一般放于内存中。
4.6.3 地址变换机构
分段比分页更容易实现信息共享
- 分页存储管理能够提高内存的利用率。
- 分段存储管理能很好地满足用户需要。
4.6.4 段页式存储管理方式
1、基本原理
先将用户程序分成若干段,再把段分成若干页
2、地址变换过程