操作系统 5 物理内存管理:连续内存分配
5 物理内存管理:连续内存分配
5.1 内存层次
在该表中,明显处理器中的L1缓存是最快的,有3.6GHz的访问速度,L2缓存较快。其中L1,L2为高速缓存。以上为硬件MMU控制的内存。
在高速缓存未命中时,就需要去内存中查找。若缺页,则需要到外存,也就是虚拟内卒中查找。这一切都是由操作系统控制管理的。
5.2操作系统中的内存管理
特点如下:
- 抽象:逻辑地址空间。逻辑地址空间可以使各进程所占有的内存按照逻辑连续的方式存储,方便进程进行调用。
- 保护:独立地址空间。不能让进程2去访问或修改进程1的内存,因此需要每个地址空间都是独立的。
- 共享:访问相同内存。所有进程都需要用到操作系统内核。如果为每个进程都分配操作系统内核的话,内存利用率就不高了。
- 虚拟化:更大的地址空间。当内存不够时,需要把数据存到外存去。外存就是虚拟内存。
综上,物理地址空间没有必要连续,但是逻辑地址空间是连续的。
5.3 操作系统中的内存管理方式
有如下四种:
- 重定位:把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。
- 分段
- 分页
- 虚拟存储:目前多数系统(如linux)采用按需页式虚拟存储
实现高度依赖硬件
- 与计算机存储架构紧耦合
- MMU(内存管理单元):处理CPU存储访问请求的硬件
逻辑地址生成
地址生成时机和限制
编译时:假设起始地址已知。如果起始地址改变,必须重新编译。
加载时:如编译时起始位置未知,编译器需生成可重定位的代码。加载时,生成绝对地址。
执行时:执行时代码可移动,需地址转换(映射)硬件的支持。
地址生成过程
CPU
- ALU:需要逻辑地址的内存内容
- MMU:进行逻辑地址和物理地址的转换
- CPU控制逻辑:给总线发送物理地址请求
内存
- 发送物理地址的内容给CPU(读)或接收CPU数据到物理地址(写)
操作系统
- 建立逻辑地址LA和物理地址PA的映射
地址检查
内存空间保护的实现,是通过CPU硬件堆用户模式所产生的每一个地址与寄存器的地址进行比较来完成。
基地址寄存器含有最小的合法物理内存地址,而界限地址寄存器决定了范围的大小。
5.4 连续内存分配和内存碎片
连续内存分配:给进程分配一块不小于指定大小的连续的物理内存区域
内存碎片:不能被利用的空闲内存
- 外部碎片:分配单元之间的未被使用内存。下图中蓝色部分都是外部碎片。
- 内部碎片:分配单元内部的未被使用内存(假设给一个进程分配了500K的内存,而该进程只使用了400K,那未被使用的100K就是内部碎片),取决于分配单元大小是否要取整。
5.4.1 动态分区分配
当程序被加载执行时,分配一个进程指定大小可变的分区(块、内存块),且分区的地址是连续的。
操作系统需要维护的数据结构
- 所有进程的已分配分区
- 空闲分区(Empty-blocks)
动态分区分配策略
- 最先匹配(First-fit)
- 最佳匹配(Best-fit)
- 最差匹配(Worst-fit)
5.4.2 最先匹配策略
思路:分配n个字节,使用第一个可用的空间比n大的空闲块。
如:分配400字节,使用第一个1KB的空闲块
原理/实现
- 空闲分区列表按照地址顺序排序
- 分配过程时,搜索一个合适的分区
- 释放分区时,检查是否可与临近的空闲分区合并
优点
- 简单
- 在高地址空间有大块的空闲分区
缺点
- 外部碎片
- 分配大块时较慢
5.4.3 最佳匹配策略
思路:
分配n字节分区时,查找并使用不小于n的最小空闲分区
示例:
分配400字节,使用第3个空闲块
原理/实现
- 空闲分区列表按照大小排序
- 分配时,查找一个合适的分区
- 释放时,查找并合并临近的空闲分区(如果找到) 该步骤较慢
优点
- 大部分分配的尺寸较小时,效果很好
- 可避免大的空闲分区被拆分
- 可减小外部碎片的大小
- 相对简单
缺点
- 外部碎片
- 释放分区较慢
- 容易产生很多无用的小碎片
5.4.4 最差匹配策略
思路
分配n字节,使用尺寸不小于n的最大空闲分区
示例
分配400字节,使用第2个空闲块(最大)
原理/实现
- 空闲分区列表按由大到小排序
- 分配时选最大的分区
- 释放时,检查是否可与临近的空闲分区合并,进行可能的合并,并调整空闲分区列表顺序
优点
- 中等大小的分配较多时,效果最好
- 避免出现太多的小碎片
缺点
- 释放分区较慢
- 外部碎片
- 容易破坏大的空闲分区,因此后续难以分配大的分区
5.5 碎片整理
5.5.1 紧凑(compaction)
碎片整理:通过调整进程占用的分区位置来减少或避免分区碎片。
碎片紧凑
- 通过移动分配给进程的内存分区,以合并外部碎片。
- 条件:所有的应用程序可动态重定位
- 需要解决的问题:何时移动?开销?
- 一种解决办法是将所有进程移到内存的一端,而将所有的孔移到内存的另一端,以生成一个大的空闲块。这种方法开销较大
- 另一种解决方法是允许物理地址空间为非连续,这样只要有物理内存就可为进程分配。这种方案有两种互补的实现技术,分页和分段。这两种技术也可合并。
5.5.2 分区对换
通过抢占并回收处于等待状态进程的分区(将其移到外存中去),以增大可用内存空间。
需要解决的问题:交换哪个(些)程序?
5.6 伙伴系统(Buddy System)
整个可分配的分区大小为 \(2^U\)
需要的分区大小为 \(2^{U-1} \le s \le 2^U\) 时,把整个块分配给该进程
如 \(s \le 2^{i-1}\), 将大小为 \(2^i\)的当前空闲分区划分成两个大小为 \(2^{i-1}\)的空闲分区
重复划分过程,直到 \(2^{i-1} \lt s \le 2^i\) ,并把一个空闲分区分配给该进程。
5.6.1 伙伴系统的实现
数据结构
- 空闲块按大小和起始地址组织成二维数组
- 初始状态:只有一个大小为 \(2^U\)的空闲块
分配过程
- 由小到大在空闲块数组中找最小的可用空闲块
- 若空闲块过大,对可用空闲块进行二等分,直到得到合适的可用空闲块
5.6.2 伙伴系统中的内存分配
释放过程
- 把释放的块放入空闲块数组
- 合并满足合并条件的空闲块
合并条件
- 大小相同,都为 \(2^i\)
- 地址相邻
- 低地址空闲块起始地址为 \(2^{i+1}\)的位数