内存的地址
绝对装入
静态重定位
装入的时候转换定位,必须全部分配,且不能移动
动态重定位
链接
覆盖和交换
1. 覆盖(Overwrite)在内存管理中的作用
定义:在内存管理中,覆盖通常指用新的数据直接替换已有的内存区域内容,而不释放原来的内存空间。
应用场景:
- 堆区数据的复用:在动态分配内存的情况下(例如
malloc
或 new
操作),可以在分配的内存上进行覆盖操作,避免反复分配与释放内存。例如,内存池(memory pool)技术会复用已分配的内存区域,通过覆盖存储新数据。 - 缓冲区覆盖:在网络或文件I/O处理中,为了减少内存分配次数,可以对缓冲区覆盖写入新数据。
注意事项:
- 内存泄漏:如果在覆盖前没有释放指针或对象指向的旧数据,可能导致内存泄漏。,对用户不够透明
- 安全问题:覆盖可能会导致原有数据被意外修改或破坏,因此需要特别小心,避免覆盖掉有效数据。
示例:
在堆区覆盖一个数组:
int* arr = new int[10];
for (int i = 0; i < 10; ++i) {
arr[i] = i;
}
for (int i = 0; i < 10; ++i) {
arr[i] = i * 2;
}
delete[] arr;
2. 交换(Swap)在内存管理中的作用
定义:在内存管理中,交换指将两个内存区域的数据对调,而不创建新的空间。这种操作通常在避免内存分配、提升效率上有重要作用。
将磁盘分为文件区和对换区,对换区更快
应用场景:
- 内存分页和分段管理:操作系统的内存管理使用交换技术将内存数据移至硬盘或从硬盘移回内存(称为“换页”或“页面交换”),用于管理虚拟内存并减少内存消耗。
- 数据排序:在快速排序或堆排序中,交换两个内存块的数据,避免频繁分配与释放内存,提升效率。
- 容器内存重排:如在C++的
std::swap
函数中,两块内存的数据可以直接交换,避免重新分配内存空间。
注意事项:
- 数据完整性:交换要确保数据不会被误改或丢失,因此通常需要辅助变量或指令来保障数据的安全性。
- 性能:在交换较大内存块的数据时,直接交换可能导致效率低下,因此可以考虑浅拷贝或指针交换。
示例:
交换两个指针指向的内存块内容:
int* a = new int(5);
int* b = new int(10);
int temp = *a;
*a = *b;
*b = temp;
delete a;
delete b;
连续分配管理方式
固定分区分配的关键概念
固定分区分配是一种内存管理方式,将内存划分为若干固定大小的分区,每个分区只能分配给一个进程。这种方法通常用于早期操作系统和一些嵌入式系统中,适合并发数较低且程序大小较稳定的应用场景。
优点
- 管理简单:分区大小固定,内存管理结构简单,易于实现。
- 快速分配:因为每个分区大小固定,分配和回收内存的速度较快,适合实时系统。
- 减少碎片化:与动态分区相比,固定分区在一定程度上可以避免内存碎片化。
缺点
- 内存浪费:当进程所需内存比分区小得多时,剩余的内存无法再利用,称为“内部碎片”。
- 缺乏灵活性:每个分区大小固定,无法动态扩展或缩小,进程可能因为不适合的分区而无法加载。
- 并发限制:分区数决定了最大并发进程数,分区少则并发进程受限。
示例
假设系统内存为 1 GB,分为 4 个固定大小的分区:每个分区 256 MB。每次启动进程时,系统会寻找空闲分区分配给进程。例如:
- 进程 A 需要 200 MB,分配给 256 MB 的分区 1,剩余 56 MB 无法利用,形成内部碎片。
- 进程 B 需要 300 MB,无法分配任何分区,因为每个分区只有 256 MB 的容量,分区不足则导致分配失败。
动态分区分配是一种内存管理方式,与固定分区分配不同,它不预先划分固定大小的分区,而是根据进程的需求动态分配合适大小的内存区域。这种方法适合现代多任务操作系统,能够更灵活地利用内存资源。
动态分区分配的关键概念
-
分区动态创建和释放
在动态分区分配中,内存没有固定的分区划分,系统根据进程的内存需求实时创建相应的内存块。每个进程的内存块分配结束后,会释放回内存池供其他进程使用。
-
内存分配算法
动态分区分配常用以下几种算法来管理内存空闲块:
- 首次适配(First Fit):从低地址开始,找到第一个足够大的空闲块分配给进程。这种方法简单快速,但容易在低地址区域产生碎片。
- 最佳适配(Best Fit):在所有空闲块中选择最小的且刚好能满足要求的块,目的是减少内存浪费。但由于空闲块较小,可能导致更多的很小的碎片。
- 最坏适应(Worst Fit):选择最大的空闲块分配给进程,目的是让剩余空间更大,便于后续使用,但效率较低,容易浪费内存。但容易导致大进程无法获取资源
- 邻近适配(Next Fit):类似首次适配,但每次从上次查找到的位置继续查找,适合减少低地址区域碎片的堆积。类似循环列表,不断去寻找。
-
空闲块管理
动态分区分配需要实时跟踪和管理内存中的空闲块。常见的管理方式包括链表和位图:
-
内存合并和碎片整理
由于动态分区会产生碎片化问题,因此需要进行合并和碎片整理。当一个内存块被释放时,系统检查相邻的内存块是否也是空闲块,如果是,就合并成一个更大的空闲块。碎片整理(如压缩内存)则是将分散的空闲块集中,以便于大块内存的分配。
优点
- 灵活性高:内存分配基于进程需求大小,可变分区大小使得系统更灵活。
- 减少浪费:相较固定分区分配,动态分区分配可以减少内部碎片,因为分配的内存块大小与需求更接近。
缺点
- 外部碎片:动态分配内存时,可能会产生很多小的、不连续的空闲块,导致外部碎片化。
- (内部碎片)是分配给进程的空间没有用上
- 管理复杂性:需要维护空闲块的信息,增加了系统开销;而碎片整理也需要额外的系统资源和时间。
- 分配效率影响:在分配和释放内存时,需要查找适合的空闲块,算法复杂度较高,可能影响分配速度。
示例
假设一个系统有 1000 KB 的内存,当前情况如下:
- 进程 A 申请了 200 KB;
- 进程 B 申请了 300 KB;
- 进程 C 申请了 100 KB;
当前剩余的空闲块为:
如果新的进程 D 申请 150 KB,系统可能会选择第二个空闲块分配给 D(基于首次适配算法)。分配后:
- 进程 D 占用空闲块 2 的 150 KB,剩余 150 KB 变为新的空闲块。
当进程 A 释放其内存后,系统可以将释放的 200 KB 和空闲块 1 的 100 KB 合并为一个新的 300 KB 的空闲块。
基本分页存储管理
内存空间分为大小相等的分区,每个分区一个页框(页帧,内存块,物理块,物理
基本地址变换机构
页表寄存器
- CPU从页表寄存器中获取页表基地址 0x3000。
- 在页表中使用页号 0x1 找到对应的物理页框地址,例如 0x4000。
- 将物理页框地址 0x4000 和页内偏移 0x234 合并,得到物理地址 0x4234。
快表的工作原理
-
TLB查找
- 命中(Hit):如果快表中存在所需的地址映射,则直接返回对应的物理地址。
- 未命中(Miss):如果快表中没有相应的映射,则需要访问页表,从中获取映射关系,并将其加载到快表中,完成地址转换。
-
LRU替换算法
由于快表容量有限,不能存储所有的页表项。因此,常用**最近最少使用(Least Recently Used,LRU)**算法来替换不常访问的页面,将其移出快表,腾出空间供新页面使用。
-
快表的优势
- 减少内存访问次数:TLB将最近使用的地址映射关系缓存到CPU内部,避免频繁访问内存中的页表。
- 提高访问速度:由于TLB位于CPU内,访问速度远快于内存,因此快表命中时能显著加快地址转换。
- 减少延迟:对系统性能要求较高的场景(如多任务操作系统和实时系统),TLB可以减少页面管理带来的延迟。
快表的局限性
- 缓存容量有限:TLB只能存储一小部分页表项,无法完全覆盖大规模的内存需求。
- 缺页中断:当快表和页表中都没有目标地址时,CPU会发生缺页中断,需要操作系统将缺页从外存(如磁盘)加载到内存,产生较高的延迟。
- 替换开销:TLB需要动态管理页面的替换策略,虽然大多数情况下LRU算法效率较高,但在特定情况下可能影响性能。
TLB的工作示例
假设有以下内存访问过程:
- 虚拟地址:
0x1234
- 页号(从虚拟地址中分解得到):
0x12
- CPU检查TLB,寻找页号
0x12
的映射关系。
- 若找到,则直接使用TLB中的物理地址完成转换。
- 若未找到,则访问页表,获取
0x12
对应的物理页框地址,并将该映射关系加载到TLB中。
- 之后的相同地址访问请求,可直接从TLB中命中,节省查找页表的开销。
二级列表
二级页表的结构与工作原理
在二级页表中,页表被分为两个层级:
-
-
二级页表的地址转换过程
-
-
二级页表查找
根据次级页号部分,在选定的次级页表中找到页框号。
-
示例
- 一级页号:10位(用于查找一级页表)
- 二级页号:10位(用于查找二级页表)
- 页内偏移:12位(用于计算页内的具体地址)
- CPU使用虚拟地址的前10位(顶级页号)索引一级页表,找到对应的二级页表的起始地址。
- 接着使用虚拟地址的中间10位(次级页号)在二级页表中查找对应的物理页框号。
- 最后,将物理页框号与虚拟地址的最后12位页内偏移组合,计算出物理地址。
注意各级页表的大小不能超过一个页面
优点
- 减少内存消耗:只需将正在使用的部分页表加载到内存中,避免为每个进程分配整页的页表,降低内存开销。
- 提高地址空间灵活性:适合于较大虚拟地址空间,便于扩展。
- 按需分配内存:系统仅在需要时为虚拟地址空间分配实际的物理页,未使用的页表不会占用内存。
缺点
- 增加访问开销:二级页表需要访问两级索引,可能会稍微增加地址转换时间。可以通过**TLB(快表)**进行缓存来提高效率。
- 管理复杂:需要维护多个页表,并且在内存中占据更多的表项和地址管理结构。
基本分段存储管理
基本分段的概念
在分段存储管理中,内存被划分为多个大小可变的段,每个段有一段基址(base)和段长(limit):
- 段基址:指定段在物理内存中的起始地址。
- 段长:指定段的大小(字节数),用于防止越界访问。
分段存储管理的地址结构
- 段号(Segment Number):用于标识段,系统根据段号在段表中找到段的基址。
- 段内偏移量(Offset):表示该段内的相对地址,即段内具体访问的位置。
- CPU从段表中查找段号
s
,获取该段的基址和段长。 - 检查段内偏移量
d
是否小于段长,以防止越界。 - 将基址与偏移量相加,得到实际的物理地址。
如果 d
超出段长,则触发越界错误。
分段存储管理的结构
-
段表(Segment Table)
- 每个进程有一个独立的段表。
- 每个段表条目包括段基址和段长。
-
段表寄存器(Segment Table Register, STR)
- 存储段表在内存中的起始位置。
- 进程切换时,更新段表寄存器以指向新进程的段表。
分段管理的优点
- 更贴合逻辑结构:分段管理按进程逻辑结构划分不同的段,更符合程序设计逻辑(代码段、数据段、堆栈段等)。
- 便于共享和保护:不同进程可以共享相同的代码段或数据段,例如只读代码段,减少内存冗余。
- 灵活的内存分配:段的大小可变,便于动态增长或收缩,内存利用率更高。
分段管理的缺点
- 外部碎片:段的大小可变,释放后可能产生不连续的空闲内存块,导致外部碎片。
- 地址转换开销:每次访问内存时都需要查找段表,增加了地址转换的时间开销。
- 复杂的段表管理:每个进程都需要维护段表,且段的长度可变,导致内存管理复杂度增加。
基本分段示例
- 段0:代码段,长度为 80 KB。
- 段1:数据段,长度为 120 KB。
- 段2:堆栈段,长度为 40 KB。
对应的段表如下:
段号 | 基址(Base) | 长度(Limit) |
---|
0 | 7K | 80K |
1 | 3K | 120K |
2 | 6K | 40K |
- CPU从段表中查找段2的基址
40K
和长度 6K
。 - 检查偏移
1024
是否小于段长 6K
,确认合法。 - 计算物理地址:
40K + 1024
。
段页式分配管理(Segmented-Paged Memory Management)
段页式分配管理的基本原理
-
-
页
每个段再细分为固定大小的页,分页管理通过将每个段进一步分解为等大小的页面,将段式管理的外部碎片问题转化为内部碎片问题。
段页式地址转换过程
-
段表查找
- 根据段号,系统在段表中找到对应段的页表地址和段的大小限制。
- 确保页号不超出该段的大小范围。
-
页表查找
-
- 将物理页框号与页内偏移量组合,计算得到最终的物理地址。
示例
-
段表查找段2
- 在段表中查找段号2,找到该段的页表起始地址和段长。
- 确认页号3在段的范围内。
-
页表查找页3
- 使用页表基址和页号3,在页表中找到物理页框号(假设为
7000
)。
-
以下是段式管理、页式管理和段页式管理的对比,包含优点和缺点:
特性 | 段式管理 | 页式管理 | 段页式管理 |
---|
基本单位 | 段(大小不固定) | 页(固定大小) | 段和页相结合 |
逻辑划分 | 根据进程的逻辑部分(代码、数据等) | 无逻辑划分,以页为单位 | 先按逻辑划分为段,再将每段分页处理 |
内存分配方式 | 动态分配,每段大小可变 | 固定分配,每页大小相同 | 每段大小可变,每段内固定大小的页 |
地址结构 | 段号 + 段内偏移 | 页号 + 页内偏移 | 段号 + 页号 + 页内偏移 |
段表/页表 | 段表 | 页表 | 段表和页表 |
外部碎片 | 存在外部碎片 | 不存在外部碎片 | 减少外部碎片 |
内部碎片 | 较少 | 可能产生内部碎片 | 页内可能存在内部碎片 |
地址转换速度 | 快(只需查找段表) | 较快(查找页表) | 较慢(需查找段表和页表) |
内存利用效率 | 较低(可能产生外部碎片) | 较高(消除外部碎片) | 高效(减少外部碎片) |
应用场景 | 早期系统,支持按逻辑划分 | 现代系统,支持虚拟内存管理 | 复杂内存管理系统,如现代操作系统 |
共享和保护 | 可以对每段独立设置保护和共享 | 共享较困难 | 段内可以共享,且每段有独立权限 |
管理开销 | 较低 | 较低 | 较高(需维护段表和多个页表) |
优点 | 支持逻辑划分,方便共享和保护 | 消除外部碎片,提高内存利用率 | 结合分段和分页,减少外部碎片,支持灵活共享与保护 |
缺点 | 易产生外部碎片,内存利用率较低 | 难以实现共享,管理灵活性较差 | 地址转换开销大,管理结构复杂 |
其它内存管理知识点
-
- 静态装入是指在编程阶段就把物理地址计算好。
-
- 可重定位是指在装入时把逻辑地址转换成物理地址,但装入后不能改变动态重定位是指在执行时再决定装入的地址并装入,装入后有可能会换出,所以同一个模块在内存中的物理地址是可能改变的。
-
- 动态重定位是指在作业运行过程中执行到一条访存指令时,再把逻辑地址转换为主存中的物理地址,实际中是通过硬件地址转换机制实现的。
-