目录
7.1 内存管理的功能
7.1.1 内存分配
内存分配的3种基本形式:
- 直接指定:编写程序时确定地址
- 静态分配:程序装入内存时确定地址
- 动态分配:程序执行时确定地址
7.1.2 地址转换
1. 空间的概念
地址空间:程序编译后,设置其地址从0开始,称为逻辑地址,逻辑地址的集合为地址空间。
存储空间:程序在主存中的实际位置称为物理地址,物理地址的集合为存储空间。
对于地址空间和存储空间,有2点需要注意:
- 地址空间的大小由计算机体系结构决定,如16位的机器,地址范围则为 0 ~ 64K ;
- 存储空间是指能够访问的主存的范围,大小由计算机的内存来决定。
例题:在一台电脑上安装了32位的操作系统,配置1GB的内存,问物理地址和逻辑地址最大分别是多少?
答:物理地址最大是2^30,逻辑地址最大是2^32。
2. 地址转换
地址转换是作业逻辑地址到物理地址的转换,又称为重定位。
7.1.3 存储保护
地址保护:一个进程不能转移到另一个进程地址上去执行。
权限保护:对于不同的进程有不同的存取权限,确保不能越权执行/访问。
7.1.4 存储共享
若多进程执行同一个程序,有两种考虑:
- 允许每个进程访问该程序的同一个副本;
- 让每个进程有自己单独的副本。
7.1.5 存储扩充
请求调入功能:把程序的一部分装入内存,使其先运行,在运行的过程中随时请求操作系统把需要的部分调入内存。
置换功能:把不需要的部分换出主存,以装载需要的部分。
7.2 程序的链接和加载
记住四个步骤:编译、链接、加载、运行;
链接后形成逻辑地址,加载后形成物理地址。
7.2.1 程序的链接
链接:由链接程序将编译后形成的一组目标模块和所需的库函数链接在一起,形成一个完整的装入模块。
链接的分类
- 静态链接:在程序运行前就链接好,以后不再拆开;
- 装入时动态链接:装入内存时边装入边链接;
- 运行时动态链接:把某些外部模块的链接推迟到需要该模块时。这样可以更容易地并入已改变或已升级的目标模块,动态链接文件为自动代码共享提供了条件。
7.2.2 程序的加载
1. 加载器的功能
加载器的功能就是给程序的指令和数据分配物理地址,所以加载又称为装入,加载器又称为装入程序。
加载可以在程序生命周期的不同时刻发生,包括程序的开发、编译、加载和运行:
2. 装入方式分类
- 绝对装入方式:直接根据目标代码地址装入内存,不用修改;
- 可重定位方式:在加载时进行静态重定位,也就是进行了修改,使得物理地址不等于逻辑地址;
- 动态运行时装入方式:目标模块装入内存后,地址仍然是相对地址,把相对地址转换为物理地址这个操作推迟到程序真正执行时才进行。
7.3 连续分配方式
连续分配就是为一个用户程序划分连续的内存空间。
7.3.1 单一连续分配
将内存分为两部分即可,因为单一连续分配只能运行一个用户程序:
7.3.2 固定分区分配
1. 特点
- 操作系统初始化时把内存空间划分为多个分区;
- 运行时不改变分区大小和数量;
2. 划分固定分区的两种方法
- 分区大小相同,所有分区大小相等;
- 分区大小不同,但是运行过程中大小不能改变;
3. 内存管理
使用内存分配表来表示,例如:
分区号 | 起始地址(K) | 大小(KB) | 状态 | 进程名 |
1 | 100 | 100 | 已分配 | P2 |
2 | 200 | 200 | 已分配 | P8 |
3 | 400 | 300 | 未分配 | |
4 | 700 | 100 | 已分配 | P12 |
这样做的优点是简单,缺点是内存利用率不高。
4. 分配策略
如果有多个输入队列,则为每个队列分配一个分区;如果只有单个输入队列,就为这个队列中的各个进程分配不同的分区。
7.3.3 动态分区分配
1. 特点
- 事先不划定分区大小;
- 根据进程大小动态分配内存空间;
一般会提供一个申请和释放的活动队列,顺着队列进行动态分配即可。
2. 内存管理
有两种形式,空闲分区表和空闲分区链,一般用的是表。
空闲分区表的格式如下:
起始地址 | 大小 |
…… | …… |
对于首次适应算法,空闲分区表从上到下应当按照空闲分区的起始地址从小到大填入;
对于最佳适应算法,则应当按照空闲分区的大小从小到大依次填入。
3. 分配策略
首次适应算法
算是这几个里面性能最好的了,找到第一个能放下的空闲分区就直接放进去:
下次适应算法
又叫循环首次适应算法,每次在上一次放进的空闲分区的下一个分区开始查找放得下的分区,查找到最后一个完后回到开头继续找,所以叫循环,如下:
最佳适应算法
每次寻找和进程大小最相近且放得下进程的空闲分区:
最坏适应算法
和最佳相反,每次把进程放到可以放得下的最大的空闲分区里:
7.3.4 可重定位分区分配
1. 内存紧凑
将离散的分区合并成一个大分区:
2. 重定位
7.3.5 交换和覆盖
交换:把内存中暂时不用的程序和数据换出到外存,以释放内存空间,当换到外存的数再次使用时,再把它们换入内存,主要用于用户管理。
覆盖:将程序划分成若干个功能相对独立的程序段,按照其自身的逻辑结构将那些不会同时执行的程序段共享同一块内存区域,主要用于系统本身。
7.4 基本分页分配方式
分区分配主要的缺点是:
- 会产生很多碎片;
- 要求一段较大并且连续的空间。
7.4.1 页面与页表
1. 页面相关概念
- 块:内存划分大小相同连续的部分;
- 页:作业逻辑地址划分成一系列相同大小部分;
- 为作业分配内存时,一块存放一页;
- 一般来说,用户作业不可能是页的整数倍,所以在最后的页中可能产生 “内碎片” ,也就是不可再利用的短小内存。
2. 页表文件
3. 地址结构
4. 逻辑地址分析
7.4.2 地址变换机构
1. 基本地址变换的步骤
- 知道页面大小
- 知道页表
- 分离出页号和页内偏移量
- 根据页号从页表中取出块号(若没有对应的块号,则记为越界)
- 合成物理地址,逻辑地址是几进制,物理地址也要是几进制
2. 分页存储管理造成的资源消耗
- 内存资源:页表需要保存在内存中,占用内存资源;
- 运行性能:获取指令的过程中需要多次访问内存,一级分页管理中每次取指需要访问内存2次。(第一次是访问内存中的页表,第二次是访问内存中指令的物理地址)
3. 快表
用来保存正在运行进程的页表的子集,便于快速对表进行地址转换。
4. 多级页表
以二级页表为例,逻辑地址包括:外层页号、内层页号和页内偏移量。
例题1
页面大小为4KB,一个块号需要4B。
思路为:
页数 = 文件大小 x 页面大小
页表文件大小 = 页数 x 一个块号所需内存
页表文件页数 = 页表文件大小 / 页面大小 (向上取整)
例题2
1)页的大小是4KB,页表最大占4MB。
2)因为每页占用的空间为4K = 1000H,而且页表中的物理地址是连续的,所以物理地址3为0090 1000H;
因为代码段长度是8KB,从第一题中可以看出,一页是4KB,所以代码占两页;
将代码段的起始逻辑地址 0000 8000H 转换为二进制,即为:
0000 0000 0000 0000 1000 0000 0000 0000H
根据题目,后12位为偏移量,则代码段对应的两个页号为 0000 0008H 和 0000 0009H,
所以8号页就是1号块,9号页就是2号块,
8号页表项物理地址 = 0020 0000H + 8 * 4 = 0020 0020H
9号页表项物理地址 = 0020 0000H + 9 * 4 = 0020 0024H
块号代表真正的物理地址,而一页是4KB,12位,
已知块号1的物理地址是 0090 0000H,因此块号1 = 00900H,块号2 = 00901H
7.5 基本分段分配方式
7.5.1 段表
1. 分段管理逻辑地址结构
段号 | 段内地址 |
两者都是用来判断是否越界的。
2. 段表
7.5.2 地址变换机构
7.5.3 共享与保护
分页与分段的区别
- 面向的对象:页是信息的物理单位,是面向系统的;段是信息的逻辑单位,是面向用户的;
- 大小:页的大小是固定的;段的长度是不固定的;
- 地址维数:分页的地址空间是一维的;分段的空间是二维的;
- 碎片:页存在内碎片;段存在外碎片;
页 | 固定式分区 | 内碎片 | 不可再用 |
段 | 可变式分区 | 外碎片 | 可以再用 |
课后测习题解析
链接后形成逻辑地址;
编译后形成物理地址。
因为是分区,所以其实就是隔离不同的内存区域,隔离就有边界,因此称为界地址保护。
分页系统的页面是为操作系统所感知的,分段系统的程序段是为用户所感知的。
3次,第一次是根据逻辑地址查询外页表,第二次是根据外页表查询内页表,第三次是根据内页表查询物理地址。
由于页的大小为2^10B,逻辑地址空间大小为2^16B,所以逻辑地址空间实际上为2^26B,因此逻辑地址是26位的;
由页的大小为2^10B,可知页内偏移量为10;
由于页表项的大小为2B,所以每页可以容纳2^9个页号,因此页号长度为9;
因此页目录号长度为7,可以容纳2^7 = 128个表项。
动态重定位是给可以动态分配内存的存储管理方案使用的,易知可变分区、页式和段式都是动态分配内存的,所以固定分区不能用动态重定位。