回顾:
- 动态重定位和保护,基址寄存器和界限寄存器(逻辑地址⇒物理地址)
- 动态分区和分段(内部⇒外部碎片)
- 空闲空间管理(位图和链表)
连续内存分配方法:
- 固定(非)大小相等的分区
- 动态分区
- 分段(单个段是连续的)
- malloc(), free()
进程所请求的内存量可以不同
随着时间的推移,记忆会变得碎片化

我们需要什么:
- 跟踪空闲内存的方法
- 当进程请求时分配空闲内存的策略
- 尽可能防止碎片化的方法
本章小结:
内存分配:
- 首次适配,最佳适配,下次适配,最差适配,快速适配
- 伙伴算法
防止碎片 - 非连续内存管理
- (分段)
- 分页(Paging)- 页表,地址转换
页表:
虚拟内存实现中用于存储物理地址和虚拟地址之间映射关系的数据结构。
内存分配(Memory Allocation)
First fit:分配第一个足够大的块
Next fit:分配足够大的下一个块,即从当前位置开始
Best fit:选择最接近所需大小的块-复杂度为O(N)
Worst fit:选择最大可能的块-复杂度为O(N)
首次适配(First Fit)
First fit从列表的开头开始扫描,直到找到一个足够大的间隙
如果(if)空间是正好适配的大小,则分配所有空间
否则(else)空间被分割:
- 第一个条目设置为所请求的大小,并标记为“已使用”(“used”)。
- 第二个条目设置为剩余大小并标记为“空闲”(“free”)。




下次适配(Next Fit)
下次适配算法保存了它到达的位置的记录:
- 它从上次停止的地方重新开始搜索
- 它为分配所有内存提供了均匀的机会(first fit集中在列表的开头)。
模拟表明,Next Fit比First Fit表现更差




First vs. Next Fit
首次适配是快速的,寻找第一个可用的洞
- 这并不意味着以后会有一个更好适配的洞
- 它可能会打破链条早期的一个大洞
下次适配不会改变这一点
最佳适配(Best fit)
最佳适配在整个链表中搜索足够大的最小洞,以满足请求
- 它比首次适配要慢
- 导致小的剩余孔(浪费内存)




最差适配(Worst Fit)
最佳适配是在空隔板上开一些小洞
最差适配找到最大的可用分区并拆分它
- 剩下的部分仍然很大(可能更有用)
- 模拟表明,最差适配在实践中并不好




快速适配(Quick Fit)
快速适配维护常用的尺寸列表
- 例如,为每个4K、8K、12K、16K等等的洞创建一个单独的列表
- 奇数大小可以放入最接近的大小中,也可以放入一个特殊的单独列表中
使用快速适配能更快地找到所需大小的洞
与最佳适配类似,它也存在产生许多小洞的问题
寻找合并(coalescing)(合并空分区)的邻居变得更加困难/耗时
合并(Coalescing)
当链表中的两个相邻条目变为空闲时,就会发生合并(连接在一起)
当一个块被释放时,检查两个邻居:
如果(if)其中一个(或两个)也是空闲的,
则(then)两个(或三个)条目通过将大小相加而组合成一个更大的块
- 链表中较早的块给出了起始点
- 将删除单独的链接,并插入单个链接







![]()
压缩(Compacting)
即使使用合并,空闲块仍然可能分布在内存中
压缩可以使用,但是很难实现并且耗时
可能需要将进程移出/交换,合并空闲空间,并将进程交换回最低可用位置
非连续内存分配
连续内存分配的不足
不同的连续内存分配方案具有不同的优点/缺点
- 单编程很容易,但会导致资源利用率低
- 固定分区有助于多编程,但会导致内部碎片
- 动态分区和分段有助于多编程,减少内部碎片,但会导致外部碎片(分配方法,合并和压缩帮助)
我们可以设计一个内存管理方案来解决连续内存方案的缺点吗?
分页(Paging)
分页使用固定分区和代码重定位来设计一种新的非连续管理方案:
- 内存被分割成更小的块
- 一个进程被分配一个或多个块(例如,一个11KB的进程将占用三个4KB的块)
- 块不必存储在连续的物理内存中
- 这个进程认为它们是连续的
非连续方案的好处包括:
- 内部碎片只减少到最后一个“块”
- 没有外部碎片(物理地址空间中的块直接堆叠在彼此的顶部)
页(page)是逻辑地址空间中连续内存的一小块,即进程所看到的
帧(frame)是物理内存中的一个小的连续块
页面和框架(通常)具有相同的大小:
- 大小通常是2的幂
- 大小范围在512字节到1Gb之间
重定位:

页表





重定位
逻辑地址(页码、页内偏移量)需要转换成物理地址(帧数、帧内偏移量)
将需要多个“基址寄存器”:
- 每个页都有一个“基址寄存器”,用来标识相关帧的起始位置
- 必须为每个进程维护一组基址寄存器
基址寄存器集存储在页表中
页表将页码映射到帧号(逻辑地址⇒物理地址):frameNumber=f(pageNumber)
页码用作页表的索引,其中列出了相关帧的编号
从帧数1可以计算出基数(偏移量保持不变),用作索引页表,其中列出了相关帧的编号
每个进程都有自己的页表,其中包含自己的一组“基址寄存器”。
操作系统维护一个空闲帧列表
理解
- 动态分区中的内存分配、合并和压缩
- 分页、页表和地址转换
问题:
给定一台使用分页的64位机器,页面/帧大小为4KB。
- 最大帧数是多少?
物理地址空间 2⁶⁴ B
每帧 4*1024 = 4096 B
最大帧数 = 2⁶⁴ / 4096 = 2⁵² 帧(约 4.5 × 10¹⁵ 帧)。
- 页表中有多少条目?
虚拟地址空间 2⁶⁴ B,每页 4096 B
页表条目数 = 2⁶⁴ / 4096 = 2⁵² 个条目(与最大帧数相同)。
- 在一个17KB的进程中有多少页?
17 KB = 17 × 1024 = 17 408 B
每页 4096 B
页数 = ⌈17 408 / 4096⌉ = 5 页。
- 在最后一帧中浪费了多少内存?
实际占用 17 408 B,5 页共 5 × 4096 = 20 480 B
浪费 = 20 480 − 17 408 = 3072 B(3 KB)。
864

被折叠的 条评论
为什么被折叠?



