操作系统存储管理
操作系统存储管理:从地址结构到页面置换的完整知识体系
本文系统整理页式、虚拟页式、分段、段页式、快表、页面置换、缺页计算和连续分区分配等高频考点,并集中澄清 LRU、LFU、NRU 等容易被中文名称误导的概念。
一、先建立总框架
存储管理题看似零散,实际上主要回答五个问题:
- 一个逻辑地址由哪些部分组成?
- 逻辑地址怎样转换成物理地址?
- 页面不在内存时,系统怎样调入和淘汰页面?
- 程序的访问顺序为什么会影响缺页次数?
- 物理内存中的空闲空间怎样分配?
对应的知识结构如下:
| 问题 | 核心知识 |
|---|---|
| 地址如何拆分 | 页号、页内偏移、段号、地址位数 |
| 地址如何转换 | 页表、段表、MMU、基址寄存器、快表 |
| 页面如何淘汰 | OPT、FIFO、LRU、LFU、NRU、Clock |
| 缺页次数如何计算 | 页框数、访问序列、数组存储顺序、局部性 |
| 空闲内存如何分配 | 首次、下次、最佳、最坏适应算法 |
做题时,第一步不是代公式,而是先确认题目问的是“页数”“页面大小”“进程总容量”“物理地址”还是“缺页次数”。
二、页式地址结构:总地址 = 页号 + 页内偏移
假设逻辑地址长度为 (n) 位,页内偏移占 (d) 位,则:
逻辑地址 = 页号(n-d位) | 页内偏移(d位)
由此得到三个最常用公式。
1. 最大页面数
页号有 (n-d) 位,所以最多有:
[
2^{n-d}\text{ 页}
]
例如,地址长度为3字节,即24位,页内偏移占10位:
[
页号位数=24-10=14
]
[
最大页面数=2^{14}=16384
]
2. 页面大小
若系统按字节编址,一个地址对应1字节,则:
[
页面大小=2^d\text{ 字节}
]
例如页内偏移为10位:
[
页面大小=2^{10}\text{B}=1\text{KB}
]
如果一个地址对应 (k) 字节,则必须乘上每个地址包含的字节数:
[
页面大小=2^d\times k\text{ 字节}
]
例如页内偏移为10位,每个地址访问4字节:
[
2{10}\times4=2\times22=2\text{B}
]
3. 进程最大容量
若按字节编址,(n) 位逻辑地址能够表示 (2^n) 个字节地址,所以:
[
进程最大容量=2^n\text{ 字节}
]
也可以通过“页数乘页面大小”计算:
[
2{n-d}\times2d=2^n
]
以24位地址、10位页内偏移为例:
| 问法 | 结果 |
|---|---|
| 最多有多少页 | (2^{14}) 页 |
| 每页多大 | (2^{10}) B |
| 进程最大多少字节 | (2^{24}) B |
这三种问题使用的是同一组数据,但答案并不相同。
三、逻辑地址到物理地址:操作系统管理,MMU执行
页式系统中,逻辑地址由页号和页内偏移组成。页表负责把逻辑页号映射成物理页框号:
逻辑地址:页号 p | 页内偏移 d
↓ 查页表
物理地址:页框号 f | 页内偏移 d
物理地址公式为:
[
物理地址=页框号\times页面大小+页内偏移
]
页内偏移在转换前后保持不变,改变的只是页号到页框号的映射。
谁负责地址转换?
- 操作系统负责创建、维护页表,分配页框,并处理缺页中断。
- MMU(内存管理单元)负责程序运行期间的实际地址转换。
因此,“地址转换完全由操作系统逐次完成”是错误的。更准确的说法是:操作系统管理转换所需的数据结构,硬件 MMU 执行高频转换。
页表基址寄存器在进程运行时指向当前页表;进程切换时,它的值作为进程现场的一部分被保存和恢复,相关信息通常保存在 PCB 中。
静态重定位与动态重定位
| 方式 | 转换时机 | 特点 |
|---|---|---|
| 静态重定位 | 程序装入内存时 | 一次性修改地址,装入后通常不能随意移动 |
| 动态重定位 | 程序运行时 | 每次访问时由硬件转换,可使用基址寄存器 |
| 基址寄存器 + 转换线路 | 运行时 | 动态重定位的一种硬件实现 |
“动态分区”和“静态分区”属于内存分配方式,不属于地址转换方式。
四、多级页表:页内偏移永远取地址最低位
假设一个32位逻辑地址被划分为:
页目录位移10位 | 页表位移10位 | 页内位移12位
那么页内地址就是逻辑地址最右边的12位。无论一级页表还是多级页表,页内偏移都位于低位,因为它表示页面内部的位置。
例如:
1101000110 | 1101101010 | 110001010011
^ 最右12位
将最后12位按每4位一组转换成十六进制:
1100 0101 0011 = C53H
所以该地址的页内地址为 C53H。
五、页表项中到底保存什么
简单页式管理假设页面都在内存中,页表最核心的信息是页框号。发展到虚拟页式后,页面可能不在内存,还可能被访问、修改和淘汰,因此典型页表项通常包含:
| 字段 | 含义 | 作用 |
|---|---|---|
| 页框号 | 页面所在的物理页框 | 形成物理地址 |
| 有效位/存在位 | 页面是否在内存中 | 判断是否发生缺页 |
| 修改位/脏位 | 页面调入后是否被修改 | 决定淘汰时是否写回磁盘 |
| 访问位/引用位 | 页面近期是否被访问 | 供 NRU、Clock 等算法使用 |
| 保护位 | 是否允许读、写、执行 | 内存保护 |
从简单页式发展到虚拟页式时,考试通常认为新增的是:
- 有效位(存在位);
- 修改位;
- 访问位(引用位)。
磁盘始址不一定直接保存在每一个页表项中,也可以由页号推导或由其他外存管理结构记录,所以不能一概认为它是每个页表项的必备字段。
“停止位”属于串行通信概念,“校验位”用于错误检测,都不是虚拟页式管理为了页面置换而新增的典型字段。
六、快表 TLB:减少查页表的内存访问
普通一级页表存放在内存时,一次数据访问通常需要:
- 访问内存中的页表;
- 根据页框号再次访问真正的数据。
若内存访问时间为 (m),不用快表的时间为:
[
2m
]
设快表查询时间为 (t),命中率为 (h)。采用完整计时模型:
- 快表命中:(t+m);
- 快表未命中:(t+2m)。
平均访问时间为:
[
EAT=h(t+m)+(1-h)(t+2m)
]
例如 (m=40ns)、(t=8ns)、(h=90%):
[
EAT=0.9\times48+0.1\times88=52ns
]
相对于不用快表的80ns,严格计算下降:
[
\frac{80-52}{80}=35%
]
部分题库采用简化口径:未命中时仍按原来的80ns计算,不额外加入8ns快表查询时间。此时:
[
EAT=0.9\times48+0.1\times80=51.2ns
]
[
下降比例=\frac{80-51.2}{80}=36%
]
因此遇到选项只有36%而没有35%时,题库采用的是后一种简化模型。快表有多少个单元,在已经直接给出命中率且不问覆盖范围时,通常是干扰信息。
七、页面置换算法:先认英文缩写,再看对应字段
页面置换算法最容易被中文名称误导。正确的判断依据如下:
| 算法 | 英文含义 | 真正比较的内容 |
|---|---|---|
| OPT | Optimal | 将来最晚使用或不再使用 |
| FIFO | First In First Out | 最早调入内存 |
| LRU | Least Recently Used | 最近一次使用距现在最久 |
| LFU | Least Frequently Used | 统计周期内访问次数最少 |
| NRU | Not Recently Used | 访问位和修改位分类 |
| Clock | 时钟算法 | 循环扫描访问位 |
1. LRU:看“多久没用”,不是“用了几次”
LRU 的准确含义是“最近性最低的页面”,即最近一次被访问的时间距离当前最远。
中文“最近最少使用”容易被错误断句。这里的 Least 修饰的是 Recently,不是访问次数:
Least Recently Used
= 最不近期被使用
= 最近最长时间没有被使用
严格的 LRU 需要最近访问时间、时间戳、栈等历史信息。访问位只有0和1,只能说明某段时间内是否访问过,不能精确给出访问先后;调入内存时间则是 FIFO 的依据,不是严格 LRU 的依据。
有些考试题没有给最近访问时间,只给访问位和调入时间,并约定“先在访问位为0的页面中选择,再挑调入最早的页面”。这只能视为题库自定义的近似规则,不能反过来当作 LRU 的标准定义。
2. LFU:看“用了多少次”
LFU 中的 F 是 Frequently,表示频率。题目通常直接给出“T时间内访问次数”,选择次数最小的页面。
例如四个页面的访问次数分别是50、86、22、78,则应选择访问22次的页面。即使该页面访问位为1,也不矛盾:
- 访问位为1:最近某段时间至少访问过一次;
- 访问次数为22:整个统计周期内总共访问22次。
“最近访问过”和“总体访问次数最少”可以同时成立。
3. NRU:看访问位 R 和修改位 M
NRU 按照 ((R,M)) 将页面分为四类,淘汰优先级通常为:
- ((0,0)):未访问、未修改;
- ((0,1)):未访问、已修改;
- ((1,0)):已访问、未修改;
- ((1,1)):已访问、已修改。
优先淘汰 ((0,0)),因为它近期没有被使用,而且淘汰时不需要写回磁盘。
4. OPT:看未来
OPT 在发生缺页且页框已满时,淘汰“未来最晚才会再次访问”或“以后不再访问”的页面。它通常用于理论比较,因为真实系统无法预知完整未来。
例如3个空页框,页面序列为:
0, 6, 0, 1, 5, 1, 5, 4, 1, 2, 5, 2, 4, 5, 2, 3, 5, 3
发生缺页的位置为第1、2、4、5、8、10、16次访问,共7次。开始时页框为空,首次装入页面也要计算缺页。
一句话记忆
FIFO 看谁先进来
LRU 看谁最久没用
LFU 看谁使用次数最少
NRU 看访问位和修改位
OPT 看谁未来最晚再用
八、CPU 利用率、磁盘繁忙率与进程数量
虚拟存储系统中,可以通过 CPU 利用率和磁盘繁忙率判断多道程序度是否合适。
| CPU利用率 | 磁盘繁忙率 | 可能情况 | 调整方向 |
|---|---|---|---|
| 低 | 低 | 可运行进程太少,资源都在空闲 | 适当增加进程 |
| 低 | 很高 | 频繁换页,可能发生抖动 | 减少进程 |
| 高 | 高 | 资源利用较充分 | 通常不调整 |
例如 CPU 利用率50%、磁盘繁忙率3%,两者都不高,说明系统不是被磁盘拖慢,也不像发生了抖动,更可能是可运行进程太少。适当增加进程,可以在一个进程等待 I/O 时让另一个进程使用 CPU。
更换更快的 CPU 不能解决“CPU 本身有一半时间空闲”的问题;磁盘繁忙率只有3%时,更换更快的磁盘同样没有明显意义。
九、数组访问与缺页:关键是存储顺序和访问顺序是否一致
数组缺页题必须先确定:
- 数组按行还是按列连续存放;
- 一行有多少元素;
- 一页能装多少元素;
- 循环中哪个下标变化最快;
- 数据实际可用多少个页框。
对于按行存放的二维数组 A[300][100],一行有100个整数。若一页能放300个整数,则一页正好容纳3行:
第0页:第1、2、3行
第1页:第4、5、6行
……
第99页:第298、299、300行
若程序外层循环是列 j=1..100,内层循环是行 i=1..300,则固定一列时的访问页面序列为:
第0页、第0页、第0页、
第1页、第1页、第1页、
……、
第99页、第99页、第99页
因此每一列缺页100次,共100列:
[
100\times100=10000\text{次}
]
如果题库给出的答案是 (300\times100=30000),它等价于假设每一次数组访问都进入不同页面。这只有在“一页仅容纳一行,即一页存100个整数”等条件下才成立,与“一页存300个整数”矛盾。遇到这种情况应判定为题目数值修改后答案未同步,而不是强行套参考答案。
这类题体现的是局部性原理:程序访问顺序与数据连续存放顺序一致时,缺页少;跨行、跨页跳跃访问时,缺页多。
十、分段与段页式管理
1. 分段
分段按照程序的逻辑结构划分,例如代码段、数据段、栈段。逻辑地址通常表示为:
段号 | 段内偏移
段表项包含段基址和段长。地址转换与保护过程为:
- 用段号查段表;
- 检查段内偏移是否小于段长;
- 物理地址 = 段基址 + 段内偏移。
如果程序从60K开始,到124K结束,则:
[
基址=60K
]
[
限长=124K-60K=64K
]
基址寄存器保存起始位置,限长寄存器保存允许访问的范围,不应把结束地址124K直接当作限长。
2. 段页式
段页式先按照逻辑关系分段,再将每一段划分成固定大小的页:
逻辑地址:段号 | 段内页号 | 页内偏移
查找过程:段表 → 该段的页表 → 物理页框
其关键特点是:
- 逻辑上分段,便于保护和共享;
- 物理上分页,内存以固定大小页框管理;
- 每个进程有一张段表,每个段对应一张页表;
- 克服了分段方式的外部碎片,但最后一页仍可能产生内部碎片。
所以“段页式的物理内存按段管理,与可变分区相同”是错误的。
十一、连续分区分配算法
连续分区题要区分“选择哪个空闲分区”,而不是地址转换。
| 算法 | 选择规则 |
|---|---|
| 首次适应 First Fit | 从头查找第一个足够大的空闲分区 |
| 下次适应 Next Fit | 从上次查找结束位置继续寻找 |
| 最佳适应 Best Fit | 选择能满足要求的最小空闲分区 |
| 最坏适应 Worst Fit | 选择当前最大的空闲分区 |
例如初始空闲区大小为:
10KB、100KB、70KB、130KB、22KB
进程 P7 请求40KB,若系统选择130KB空闲区,留下90KB;随后 P8 请求10KB,又选择当时最大的100KB空闲区,留下90KB。这说明系统每次都选择最大的空闲区,使用的是最坏适应算法。
最佳适应在 P7 请求40KB时会选择70KB空闲区,而不会选择130KB。
十二、高频名词对照表
| 名词 | 对应信息 | 不要混淆成 |
|---|---|---|
| 页号 | 逻辑页面编号 | 页框号 |
| 页框号 | 物理内存块编号 | 页号 |
| 页内偏移 | 页面内部位置 | 页面数量 |
| 调入时间 | 页面进入内存的时间 | 最近访问时间 |
| 最近访问时间 | 上一次使用页面的时间 | 访问次数 |
| 访问位 | 某周期是否访问过,0或1 | 精确访问时间 |
| 修改位 | 页面是否被写过 | 页面是否被访问 |
| T时间内访问次数 | 统计周期内访问频率 | 页面的驻留时间 |
| 有效位/存在位 | 页面是否在内存 | 地址是否合法的所有含义 |
| 基址 | 段或分区的起始地址 | 段长 |
| 限长 | 可访问范围的长度 | 结束地址 |
十三、统一做题流程
地址位数题
- 把字节数换成位数,例如3字节等于24位;
- 总位数减去页内偏移位数,得到页号位数;
- 看清题目问页数、页大小还是总容量;
- 检查每个地址对应1字节还是多个字节。
页面置换题
- 先认英文缩写;
- 确认页框初始是否为空;
- 命中不算缺页;
- 缺页且页框未满时直接装入;
- 页框已满时,严格按照该算法选择淘汰页;
- 不要拿访问次数回答 LRU,也不要拿访问位回答 LFU。
数组缺页题
- 写出数组在内存中的连续顺序;
- 算出一页容纳几行或几个完整数据块;
- 写出前几次访问对应的页号;
- 再按页框数和置换算法统计;
- 如果参考答案与页号序列直接矛盾,应检查题库条件是否修改错误。
分区分配题
- 列出所有空闲分区及大小;
- 按进程请求顺序逐个分配;
- 观察每次选择的是第一个、最小、最大,还是从上次位置继续;
- 每次分配后立即更新剩余空闲区。
十四、最后的核心口诀
地址总位数 = 页号位数 + 页内偏移位数
页面大小 = 页内地址数 × 每个地址的字节数
进程容量 = 页面数 × 页面大小
操作系统管理页表,MMU执行地址转换
页内偏移取逻辑地址最低位
FIFO 看进入时间
LRU 看最近一次使用时间
LFU 看统计周期内访问次数
NRU 看访问位和修改位
OPT 看未来最晚使用时间
逻辑上分段,物理上分页
最佳适应选最小,最坏适应选最大
只要先判断题目正在比较“时间、次数、状态位、地址位数还是空闲分区大小”,大多数存储管理题都可以落入上面的固定框架中。

浙公网安备 33010602011771号