Windows 内存管理:分页、分段与页面置换
本节从计算机内存的基本作用出发,比较连续分配与非连续分配,理解物理地址、逻辑地址、内部碎片和外部碎片等基础概念。
随后重点整理页式、段式和段页式存储,并结合缺页中断与 OPT、FIFO、LRU 页面置换算法,梳理操作系统管理进程地址空间的基本思路。
一、什么是内存
1. 计算机的基本组成
- 输入设备:键盘、鼠标、摄像头等;
- 输出设备:显示器、音箱等;
- 外存:硬盘等用于长期保存数据的设备;
- 内存:程序运行时临时保存程序和数据的空间;
- CPU:从内存取出数据,完成计算后再把结果写回内存。
2. 内存和外存的区别
| 对比项 | 内存 | 外存 |
|---|---|---|
| 速度 | 快 | 相对较慢 |
| 价格 | 相对较高 | 相对较低 |
| 作用 | 保存正在运行的程序和数据 | 长期保存程序和文件 |
| 断电后 | 数据丢失 | 数据仍保留 |
安装好的程序平时保存在外存中。
用户启动程序时,程序被装入内存;程序结束后,其占用的内存空间被释放。
二、为什么需要内存管理
内存管理的一个重要目的,是保障进程和操作系统的安全。
例如:
int a = 0;int* b = &a;在 32 位环境下,a 通常占 4 个字节。指针 b 应当只访问属于 a 的有效空间。如果随意移动指针并访问不属于当前变量或当前进程的地址,就可能出现:
- 访问其他变量的数据;
- 访问其他进程的数据;
- 破坏系统内核空间的数据;
- 导致当前程序崩溃;
- 导致其他程序甚至操作系统出现异常。
操作系统通过内存管理限制进程的访问范围。
程序访问无权操作的内存时,系统会阻止访问并终止或中断程序,从而保护当前进程、其他进程和操作系统。
三、内存分配方式
1. 连续分配
连续分配要求一个进程所使用的内存必须位于一段连续的物理空间中。
例如,某个程序需要 3 GB 内存,则系统必须找到连续的 3 GB 空间。
2. 非连续分配
非连续分配允许把程序拆开,分别存入不相邻的物理内存区域。
例如,一个程序需要 3 GB 内存,可以由不相邻的 2 GB 和 1 GB 空间共同组成。
四、连续分配管理
1. 单一连续分配
特点:
- 单用户、单任务;
- 内存中一次只运行一个作业;
- 作业进入内存后,要等到执行结束才释放;
- 不能实现多个进程同时驻留内存。
这种方式结构简单,适合功能单一的小型设备。
2. 固定分区分配
在程序运行前,先把内存划分成若干个大小固定的分区;程序进入时,再选择能够容纳它的分区。
当一个程序需要 1.5 GB 时,可放入 2 GB 的分区中。但是该分区剩余的 0.5 GB 无法再分配给其他进程,会形成内部碎片。
特点:
- 分区大小预先确定;
- 程序运行时不能改变分区大小;
- 通常使用静态重定位方式装入内存;
- 容易产生内部碎片。
3. 动态分区分配
动态分区不预先规定每个分区的大小,而是根据作业实际需要动态创建分区。
例如,一个程序需要 1 GB,就划出 1 GB;另一个程序需要 2 GB,就再划出 2 GB。
当中间的程序结束后,内存中可能出现多个互不相邻的小空闲区。虽然这些空闲区的总大小足够,但因为它们不连续,仍然无法分配给需要较大连续空间的新进程。
特点:
- 分区大小可变;
- 按照作业实际大小分配;
- 比固定分区更灵活;
- 容易产生外部碎片。
五、几个重要概念
1. 物理地址
物理地址是物理内存中真实存在的地址。
内存条中的每个存储位置都有对应的物理地址。
2. 逻辑地址
逻辑地址也叫虚拟地址,是程序运行时看到和使用的地址。
例如:
int a = 10;cout << &a << endl;程序打印出来的是逻辑地址,而不是内存条上的真实物理地址。
3. 内部碎片
已经分配给进程的内存块内部,没有被实际使用的空间叫内部碎片。
4. 外部碎片
在已经分配的内存块之间,剩余但无法满足新进程连续空间要求的小空闲区叫外部碎片。
5. 两种碎片的区别
| 对比项 | 内部碎片 | 外部碎片 |
|---|---|---|
| 位置 | 已分配内存块内部 | 各分配块之间 |
| 原因 | 分配单位大于实际需要 | 空闲空间被分散 |
| 典型场景 | 固定分区、分页最后一页 | 动态分区、分段 |
六、页式存储
1. 页与块
页式存储把程序的逻辑地址空间划分为大小相同的页,把物理内存划分为大小相同的块。
- 程序中的单位叫页;
- 物理内存中的单位叫块;
- 页和块的大小必须相同;
- 一页数据正好能够放入一个物理块;
- 页号和块号均从 0 开始。
程序各页在逻辑地址空间中连续,但装入物理内存后,各块可以不连续。
2. 页表
页表记录逻辑页号与物理块号之间的对应关系。
| 页号 | 块号 |
|---|---|
| 0 | 2 |
| 1 | 3 |
| 2 | 5 |
| 3 | 7 |
程序给出逻辑地址后,操作系统先算出逻辑页号,再查询页表获得对应块号,最后计算真实物理地址。
七、缺页中断
1. 为什么会发生缺页中断
物理内存容量有限。
当运行的程序越来越多、物理块已经分配完时,系统会暂时把部分不活跃页面的数据移动到外存,从而腾出物理块供当前活跃程序使用。
以后进程再次访问被移出的页面时,页表所指向的位置已经没有该进程需要的数据,于是产生缺页中断。
2. 缺页中断处理过程
进程访问某个逻辑页 ↓发现该页当前不在内存 ↓触发缺页中断并暂停当前访问 ↓选择一个物理块中的页面换出到外存 ↓把所需页面从外存调入内存 ↓更新页表中的映射关系 ↓恢复进程运行八、页面置换算法
页面置换算法解决的问题是:发生缺页中断时,应当把内存中的哪一页换到外存。
1. OPT:最佳页面置换算法
OPT 选择在未来最长时间内不会被访问的页面换出。
优点:理论上能够获得较少的缺页次数。
缺点:实际运行时很难预知用户以后会访问哪个页面,因此它是一种理想化算法。
2. FIFO:先进先出算法
FIFO 选择在内存中驻留时间最长、最早进入内存的页面换出。
3. LRU:最近最久未使用算法
LRU 选择最长时间没有被访问的页面换出。
九、页式存储的优缺点
1. 优点
- 很好地解决外部碎片问题,只会产生内部碎片;
- 打破内存分配必须连续的要求;
- 提高主存利用率。
2. 缺点
- 需要页表及相应硬件支持;
- 会产生内部碎片;
- 地址转换需要额外系统资源;
- 当可用块数少于进程需要的块数时,需要等待或执行页面换入、换出;
- 页面交换频繁时,系统性能会降低。
十、段式存储
1. 基本思想
段式存储按照程序的逻辑意义把程序划分成若干段。
每个段具有独立、完整的逻辑意义,可以独立编写和编译,而且不同段的长度可以不同。
每一段内部的地址从 0 开始,因此:
- 段内地址连续;
- 段与段之间的逻辑地址不连续;
- 段式逻辑地址由段号 + 段内偏移组成。
2. 段表
段表保存每个段的段长和物理基址。
十一、段式存储的优缺点
1. 优点
- 不产生内部碎片;
- 可以以段为单位独立编写和编译;
- 修改一个段时,对其他段影响较小;
- 可以针对不同类型的段采取不同保护方式;
- 可以以段为单位进行共享;
- 可以通过动态链接实现代码共享。
2. 缺点
- 会产生外部碎片;
- 段的大小不固定,物理内存不一定能被完整分配;
- 虽然会产生外部碎片,但程序已经被划分为多个较小的逻辑段,因此碎片通常比整个程序连续分配时更小。
十二、段页式存储
1. 基本思想
段页式存储把段式和页式结合起来:程序先按照逻辑意义分段,每一段内部再按照固定大小分页。
段页式逻辑地址由三部分组成:段号 + 段内页号 + 页内偏移。
2. 段页式的开销
- 段式或页式只需要先查一张表,再访问主存,一般需要两次内存访问;
- 段页式要先查段表,再查页表,最后访问主存,一般需要三次内存访问;
- 段页式需要保存一张段表和多张页表,占用的表空间更多。
段页式牺牲了一部分存储空间来保存更多表结构,但可以把大型地址空间分层管理,提高在大型计算系统中的查询效率。
3. 段页式的碎片
- 段内分页,最后一页可能没有用满,因此会产生内部碎片;
- 先按逻辑意义分段,段大小不固定,因此也会继承段式存储的外部碎片问题;
- 段页式同时继承了页式和段式的优点,也继承了二者的缺点。
十三、三种非连续分配方式比较
| 对比项 | 页式存储 | 段式存储 | 段页式存储 |
|---|---|---|---|
| 划分依据 | 固定大小分页 | 按程序逻辑意义分段 | 先分段,段内再分页 |
| 单位大小 | 每页大小相同 | 每段大小可以不同 | 段大小可变,页大小固定 |
| 逻辑地址组成 | 页号 + 页内偏移 | 段号 + 段内偏移 | 段号 + 页号 + 页内偏移 |
| 地址空间 | 一维 | 二维 | 分段后再分页 |
| 映射表 | 页表 | 段表 | 段表 + 多张页表 |
| 一般访存次数 | 2 次 | 2 次 | 3 次 |
| 内部碎片 | 有 | 无 | 有 |
| 外部碎片 | 无 | 有 | 有 |
| 共享和保护 | 通常以页为单位 | 以逻辑段为单位,更自然 | 可以结合段和页进行管理 |
如果这篇文章对你有帮助,欢迎分享给更多人!





