操作系统第 3 章:内存管理
408 操作系统「内存管理」复习:核心知识点、真题题型、通用解法与易错点。
Last updated on
本章以本站 408 真题 → 操作系统 → 第 3 章 内存管理 为主,并用 408 模拟选择题 → 操作系统 → 第 3 章 补充题型。本站也明确建议按“真题确定命题方式,再用模拟题补充”的顺序复习。第 3 章真题主要分为“基本内存管理”和“虚拟内存管理”两大部分。(Any Docs)
这一章是操作系统里计算味最重、综合性最强的章节之一。
如果第 2 章进程管理的核心是“状态 + 调度 + PV”,那么第 3 章可以压缩成一句话:
CPU 给出虚拟地址以后,到底怎样找到真正的物理内存?如果页面不在内存,又怎么办?
408 会围绕这条主线不断变形。
一、先建立整章知识地图
| 模块 | 典型考法 | 重要度 |
|---|---|---|
| 地址装入、链接、重定位 | 概念选择题 | ★★ |
| 连续分配 | 首次/最佳/最坏适应、碎片、回收合并 | ★★★ |
| 伙伴系统 | 分裂、合并 | ★★~★★★ |
| 基本分页 | 页号、页内偏移、页框、物理地址 | ★★★★★ |
| 多级页表 | 页表大小、地址拆分、页表级数 | ★★★★★ |
| 分段 | 段号、段长、越界、共享 | ★★★ |
| 请求分页 | 缺页异常处理 | ★★★★★ |
| 页面置换 | FIFO、LRU、CLOCK、改进 CLOCK | ★★★★★ |
| TLB | 命中率、平均访存时间 | ★★★★★ |
| 工作集与抖动 | 工作集计算、抖动原因 | ★★★ |
| 页面缓冲 | 与置换、缺页率区别 | ★★ |
| 局部性 | 数组访问与缺页、Cache 联合 | ★★★★ |
| 综合大题 | 页表 + TLB + 缺页 + 置换 + 地址计算 | ★★★★★ |
真正应该投入最多时间的是:
分页地址转换 → 多级页表 → TLB → 缺页 → 页面置换 → 物理地址。
这几个知识点可以单独考,也非常喜欢串成一道大题。
二、整章最重要的“地址转换模型”
以后只要看到分页题,第一反应必须是:
虚拟地址 = 虚拟页号 + 页内偏移量。
设页面大小为
那么低 位永远是页内偏移。
于是:
查页表得到页框号 后:
这就是整个分页系统计算题的母公式。
例如页大小 4KB:
那么虚拟地址 2362H:
所以:
页号 = 2,
页内偏移 = 362H。
这一步必须形成条件反射。
三、题型一:链接、装入、重定位与地址保护
1. 逻辑地址什么时候形成?
【2011 408 真题】
题意是:
虚拟内存系统中最终还要把逻辑地址转换为物理地址,那么程序的逻辑地址主要在哪个阶段形成:编辑、编译、链接还是装载?
答案:链接阶段。(Any Docs)
这一题容易把“编译”和“链接”混淆。
基本流程:
源程序
→ 编译
→ 若干目标模块
→ 链接
→ 完整逻辑地址空间
→ 装入
→ 运行期间地址转换。
可以理解成:
编译解决“每个模块自己是什么”,链接解决“大家拼在一起以后在哪里”。
2. 重定位
重定位就是:
把逻辑地址映射为物理地址。
分成两种。
| 类型 | 转换时间 | 特点 |
|---|---|---|
| 静态重定位 | 装入时 | 一旦装入,通常不能随便移动 |
| 动态重定位 | 运行时 | 借助硬件地址转换,可以移动 |
模拟题还考过一个重要延伸:
紧凑为什么需要动态重定位?
因为紧凑会把已经在内存中的进程移动位置。
原来:
进程A | 空洞 | 进程B | 空洞 | 进程C
移动后:
进程A | 进程B | 进程C | 大空洞
进程 B、C 的物理地址都变了。
如果程序里面直接写死物理地址,移动以后全部失效。
所以紧凑要求:
模拟题也明确以这一点设问。(Any Docs)
3. 地址保护
【2009 408 真题】
题目问:
分区分配方式下,主要采用什么措施保护各进程的内存空间?
答案:界地址保护。(Any Docs)
常见实现:
基址寄存器 + 限长寄存器。
逻辑地址 首先判断:
合法后:
其中:
:基址;
:进程地址空间长度。
一定是:
先检查,再相加。
这个思想到了分段系统完全一样。
四、题型二:内部碎片与外部碎片
这是选择题高频陷阱。
1. 内部碎片
已经分给出了,但尚未使用完。
例如一个页框 4KB,进程最后一页只有 1KB:
[1KB有效数据 | 3KB浪费]
这 3KB 已经属于该进程,因此别人无法使用。
所以是内部碎片。
2. 外部碎片
总空闲量可能足够,但被切成很多小洞。
例如:
20KB空闲 | A | 30KB空闲 | B | 40KB空闲
总共有:
但要申请 60KB 连续空间仍然失败。
因为不存在一个 60KB 的连续空洞。
必须记住的表
| 管理方式 | 内部碎片 | 外部碎片 |
|---|---|---|
| 固定分区 | 有 | 通常不作为主要问题 |
| 动态分区 | 基本无 | 有 |
| 分页 | 有 | 无 |
| 分段 | 基本无 | 有 |
| 段页式 | 有 | 无页框层面的外部碎片 |
模拟题专门把固定分区、动态分区、分页、分段、段页式混起来问,正确判断内部碎片的关键就是:
固定大小单位分配 → 容易产生内部碎片;可变连续区域 → 容易产生外部碎片。 (Any Docs)
分页绝对不要说“页面小可以减少外部碎片”。
因为:
页面变小减少的是平均内部碎片。
但代价是:
页数增加 → 页表项增加 → 页表更大。
五、题型三:动态分区分配算法
这是典型的“画内存图”问题。
需要掌握:
| 算法 | 选择 |
|---|---|
| First Fit 首次适应 | 第一个够大的 |
| Next Fit 邻近/循环首次适应 | 从上次搜索位置继续 |
| Best Fit 最佳适应 | 最小但够用的 |
| Worst Fit 最坏适应 | 最大的 |
六、经典真题:Best Fit
【2010 408 真题】
题目给出:
主存 55MB,开始全部空闲,采用最佳适应算法:
分配 15MB
→ 分配 30MB
→ 释放 15MB
→ 分配 8MB
→ 分配 6MB
问最后最大的空闲分区是多少。(Any Docs)
开始:
分 15:
再分 30:
释放 15:
空洞为:
申请 8MB。
Best Fit 找最小但够用的。
所以使用 10MB:
现在:
再申请 6MB。
使用 15MB:
最终:
所以最大空闲区:
七、为什么 Best Fit 反而容易产生碎片?
【2019 408 真题】
问:
First Fit、Worst Fit、Best Fit、Next Fit 中,谁最容易产生小碎片?
答案是:
因为它总挑“刚刚够大”的。
例如申请 99KB。
存在:
100KB、300KB、1000KB。
Best Fit 会拿 100KB:
于是不断制造:
1KB、2KB、3KB……
这种很难再使用的小洞。(Any Docs)
八、动态分区真正难的是“释放后合并”
【2017 408 真题】
题目给出的空闲区包括:
20K 起始、40KB;
500K 起始、80KB;
1000K 起始、100KB;
200K 起始、200KB。
现在回收:
起始 60K、大小 140KB。
最好的方法不是靠想象,而是全部写成左闭右开区间。
原第一个:
回收:
后面的空闲分区:
三段刚好连续。
合并:
大小:
于是原来的四个空洞中,两个与新释放区域合并,最终只有三个空洞。
而题目还说明使用 Best Fit 且每次重新排序,所以空闲链表按大小从小到大:
80KB
→ 100KB
→ 380KB。
因此链首是:
起始 500K、80KB。(Any Docs)
这类题的统一方法
永远写:
然后检查:
前一个结束地址 = 新区起始?
新区结束地址 = 后一个起始?
是就合并。
比画随意的方块可靠得多。
九、模拟题的重要变式:从高地址端分配
本站里有一道王道模拟题:
初始用户区 [126K,512K),所有申请都从所选空闲分区的高地址端切出。
依次申请、释放后再采用 First Fit,最终最小空闲块得到 13KB。(Any Docs)
这道题最有价值的地方不是答案,而是在提醒:
“First Fit”只决定选哪个空洞,并不一定决定从空洞哪一端切。
如果题目明确:
“从高地址部分分配”
就不能习惯性从低地址端画。
十、伙伴算法:2024 已经正式考过
【2024 408 真题】
题目问:
每次释放分区时,只与“大小相同的对应空闲分区”进行合并的是哪种算法?
答案:
伙伴系统的块大小都是:
例如申请 20KB。
可能从 64KB 开始:
给用户一个 32KB。
内部浪费:
释放以后,只有它对应的那个 32KB 伙伴也空闲,才能合并:
不能随便找另一个 32KB 合并。(Any Docs)
这可能继续变式
408 很可能继续考:
给起始地址、块大小,判断两个块是不是 buddy;
或者连续申请、释放后求最终空闲块。
这属于值得准备的新考法。
十一、题型四:基本分页——本章真正的核心
分页系统有三个词必须彻底区分:
| 概念 | 属于哪里 |
|---|---|
| page 页 | 虚拟地址空间 |
| frame 页框 / 页帧 / 物理块 | 物理内存 |
| page table 页表 | page → frame 映射 |
例如:
虚拟页 5 → 页框 17。
如果页面大小为 4KB:
十二、地址位数怎么拆?
如果页大小为:
那么页内偏移 12 位。
32 位地址:
因此:
虚拟页数:
页面:
十三、页表大小怎么算?
假设:
32 位虚拟地址;
页大小 4KB;
PTE 4B。
页号 20 位,所以页表有:
项。
每项 4B:
所以一个进程如果建立完整一级页表,就可能需要 4MB。
这正是多级页表出现的原因之一。
十四、2013 408 大题:必须会
【2013 408 真题】
题目给定:
32 位逻辑地址和物理地址;
页表项 4B;
一级分页中地址格式为:
20 位页号 + 12 位页内偏移;
然后又改为:
10 位页目录号 + 10 位页表索引 + 12 位页内偏移;
最后给代码段逻辑地址和页表起始物理地址,要求继续算页表项地址和物理页框。(Any Docs)
这道题几乎就是分页计算的模板题。
第1问:页多大?
低 12 位偏移:
页表最大多大?
页号 20 位:
个页表项。
每项 4B:
第2问:二级页表地址拆分
地址:
对于虚拟地址 LA:
页目录号:
页表索引:
因为:
正好取得 10 位。
第3问:页表项物理地址
如果代码从:
00008000H
开始。
页大小:
1000H
因此:
属于虚页 8。
下一个页是虚页 9。
若页表从:
00200000H
开始,每个 PTE 为 4B。
虚页 8 的 PTE:
虚页 9:
这一招非常重要:
以后所有一级页表题本质一样。
十五、为什么要多级页表?
【2014 408 真题】
题目问多级页表的优点。
正确方向不是“转换更快”,而是:
减少页表对大块连续内存的要求,并且没用到的虚拟地址范围可以不建立下级页表。(Any Docs)
这一点非常容易错。
多级页表:
节省页表空间 / 降低连续空间要求
但代价是:
地址转换可能访问更多次内存。
例如没有 TLB:
一级页表:
查页表 + 访问数据:
次内存。
二级页表:
页目录
→ 二级页表
→ 数据。
一共:
次。
三级:
次。
因此:
十六、二级页表的经典计算
【2010 408 真题】
条件:
页面:
PTE:
2B。
整个逻辑空间:
页。
一个二级页表通常占一个页面,所以每张二级页表能放:
个 PTE。
总共需要管理:
页。
因此需要二级页表数量:
于是页目录至少有:
项。(Any Docs)
通用公式
页面大小 ,PTE 大小 :
如果要覆盖 个页面:
十七、多级页表最少要几级?
这是模拟题很好的补充。
【王道·卷八 Q27】
实际虚拟地址空间为:
页面:
PTE:
问至少几级页表。(Any Docs)
页内偏移:
位。
虚拟页号:
位。
一个页表页可以容纳:
个 PTE。
所以一级可以承担 10 位索引。
35 位 VPN 需要:
级。
因此:
统一公式
若:
虚拟地址有效位数 =
页面大小 =
PTE 大小 =
则每张页表可放:
项。
一级能索引:
位。
VPN:
位。
页表级数:
这是非常值得记住的推导式。
十八、PDBR / 页表基址寄存器到底存什么?
【2021 408 真题】
二级分页系统问:
CPU 页表基址寄存器保存什么?
答案:
不是虚拟地址。(Any Docs)
为什么?
假如 PDBR 存虚拟地址:
CPU 想查页表
→ 得先把 PDBR 的虚拟地址转物理地址
→ 转它又需要查页表
→ 但页表地址还不知道……
循环依赖。
所以必须从一个真实物理地址起步。
十九、进程切换与线程切换对 PDBR 的影响
【2018 408 大题】
题目还问:
进程切换时 PDBR 变不变?
同一进程线程切换时呢?
结论:
不同进程有不同虚拟地址空间:
同一进程中的线程共享虚拟地址空间:
这道题同时还要求由页目录号、页号和偏移重新拼出虚拟地址,以及说明改进 CLOCK 需要访问位和修改位。(Any Docs)
二十、题型五:分段
分页:
站在操作系统/物理内存角度,把东西等大小切。
分段:
站在程序员逻辑角度,把程序分成代码段、数据段、栈段……
所以分段地址:
二十一、最大段长怎么求?
【2009 408 真题】
32 位逻辑地址。
段号占 8 位。
于是段内偏移占:
位。
所以单段最大长度:
注意:
最大合法偏移是:
但“最大段长度”是:
不要差 1。(Any Docs)
二十二、分段地址转换:先越界检查!
【2016 408 真题】
段 2:
段长 300;
基址 4000;
已经在内存;
可读写。
现在访问:
段号 2,段内地址 400。
有人会直接算:
错。
必须先检查:
不成立。
因此直接:
不会得到 4400。(Any Docs)
统一流程:
二十三、共享段 / 共享页
这是近年来非常值得注意的一类。
【2019 408 真题】
P1 和 P2 共享段 S。
共享意味着:
物理内存中只保存一份。
但:
因为段号属于各自的逻辑地址空间。(Any Docs)
【2023 408 真题】
进一步改成分页。
R 和 S 共享数据 data。
在 R 中虚拟页号为 ,在 S 中为 。
对应页框:
。
因为两个进程虚拟地址空间独立:
和
不必一样。
但既然共享同一份物理数据:
所以:
这是理解虚拟内存非常好的一道题。(Any Docs)
二十四、题型六:请求分页与缺页异常
这是第二大核心。
基本分页:
所有需要页面先装进来,再运行。
请求分页:
暂时不用的页面先不装,访问到了再装。
所以页表项必须额外记录:
存在位 / 有效位。
例如:
| 页号 | 页框号 | 存在位 |
|---|---|---|
| 2 | 17 | 1 |
| 3 | — | 0 |
访问页 3:
二十五、缺页异常到底干什么?
完整逻辑是:
虚拟地址合法
→ 查页表发现页面不在内存
→ 产生缺页异常
→ OS 接管
→ 找页框
→ 必要时选择牺牲页
→ 若牺牲页已修改则写回
→ 从磁盘调入目标页
→ 修改页表
→ 必要时更新/失效 TLB
→ 重新执行发生缺页的指令
这里一个非常关键的判断:
缺页 ≠ 越界
缺页表示:
越界表示:
二十六、缺页时一定置换页面吗?
不是。
【2022 408 真题】
页面 b 不在内存,发生缺页。
问缺页处理“不一定”包含什么操作。
答案:
因为如果还有空闲页框:
直接把 b 放进去即可。
无须淘汰。(Any Docs)
所以:
缺页:
页面置换。
正确关系是:
二十七、固定分配、可变分配、局部/全局置换
【2015 408 真题】
问哪种组合不可能:
固定分配 + 全局置换。
答案:
原因:
固定分配规定:
进程页框数固定。
全局置换:
可以从系统其他进程占用的页框里选牺牲页。
那某进程拿走别人的页框之后,各进程页框数就改变了。
于是与固定分配矛盾。(Any Docs)
应该记:
| 组合 | 可否 |
|---|---|
| 固定 + 局部 | ✓ |
| 固定 + 全局 | ✗ |
| 可变 + 局部 | ✓ |
| 可变 + 全局 | ✓ |
二十八、题型七:页面置换算法
四个必须精通:
| 算法 | 淘汰谁 | 需要什么信息 |
|---|---|---|
| OPT | 未来最久不用 | 未来访问序列 |
| FIFO | 最早进入内存 | 装入时间 |
| LRU | 最久没有被访问 | 最近访问历史 |
| CLOCK | 近似 LRU | 访问位 |
| 改进 CLOCK | 综合访问/修改 | A 位 + M 位 |
二十九、OPT:理论最优
OPT:
未来最长时间不用的页面淘汰。
它能取得理论上的最少缺页次数。
但真实系统不知道未来。
所以:
三十、FIFO:只看“进来多久”
例如内存:
A 在时刻 10 装入;
B 在 20;
C 在 30。
现在缺页。
FIFO:
因为 A 最早进入。
它根本不管:
A 刚刚是不是被访问了。
三十一、LRU:只看“多久没使用”
例如最近访问:
A:时刻 100
B:时刻 80
C:时刻 120
那么:
因为 B 最近一次使用离现在最远。
三十二、2015 408:LRU 标准选择题
分配 4 个页框。
访问序列已经走到:
2,0,2,9,3,4,2,8,2,4,8,4,5。
下一页是 7。
题目问 LRU 淘汰谁。(Any Docs)
处理完成后驻留:
看它们最后一次出现的位置。
2:更早
8:之后
4:之后
5:刚刚出现。
所以:
LRU 最快方法
如果题目只问“当前淘汰谁”,不一定需要从头完整画表。
只要确定当前驻留页面,然后从访问序列末尾倒着找。
最后最晚找到的那个驻留页,就是 LRU。
三十三、FIFO 与 LRU 最容易混淆的地方
假设:
A 最早装入;
但 A 刚刚被访问。
那么:
FIFO:
仍然淘汰 A。
LRU:
不会淘汰 A。
一句口诀:
FIFO 看出生日期,LRU 看最后活动时间。
三十四、Belady 异常
【2014 408 真题】
现象:
页框增加以后,缺页次数反而增加。
可能发生在:
LRU 和 OPT 不会。(Any Docs)
考试记:
因为 LRU、OPT 具有栈性质。
三十五、CLOCK 算法
每个页面有访问位 A。
访问页面时:
缺页需要淘汰时,从指针开始扫描:
如果:
直接淘汰。
如果:
给第二次机会:
指针继续走。
所以也叫:
Second Chance 第二次机会算法。
三十六、2010 408 大题:FIFO 与 CLOCK
这道题非常值得完整掌握。
【2010 408 真题】
页大小:
进程当前 4 个页面:
| 页 | 页框 | 装入时刻 | A |
|---|---|---|---|
| 0 | 7 | 130 | 1 |
| 1 | 4 | 230 | 1 |
| 2 | 2 | 200 | 1 |
| 3 | 9 | 160 | 1 |
访问逻辑地址:
17CAH。(Any Docs)
第一步:拆页号
所以:
页 5 不在内存,缺页。
第二问:FIFO
装入时间:
页 0:130
页 1:230
页 2:200
页 3:160。
最早:
所以淘汰页 0。
页 0 原页框:
页 5 装入页框 7。
物理地址:
第三问:CLOCK
指针当前指向页框 2。
所有页面 A 都是 1。
第一次扫描:
全部从 1 清成 0。
转一圈重新回到页框 2。
现在该页:
于是淘汰页框 2 中的页 2。
页 5 装入页框 2。
物理地址:
这道题体现了本章的核心综合形式:
三十七、改进型 CLOCK
普通 CLOCK 只考虑:
访问位 A。
改进 CLOCK 再加入:
修改位 M。
四种组合:
| A | M | 含义 |
|---|---|---|
| 0 | 0 | 最近没访问、没修改 |
| 0 | 1 | 最近没访问、修改过 |
| 1 | 0 | 最近访问、没修改 |
| 1 | 1 | 最近访问、修改过 |
理想淘汰顺序考试中记:
为什么 (0,0) 最好?
没访问:
说明近期不重要。
没修改:
意味着和磁盘中的副本一致。
所以可以直接扔。
为什么 M=1 代价大?
因为页已经被改过。
内存:
新版本。
磁盘:
旧版本。
淘汰前必须:
产生额外 I/O。
2016 真题就直接考过四类的淘汰次序。(Any Docs)
三十八、2021 408:缺页 + 改进 CLOCK + 地址转换
题目给:
页大小 4KB;
2 个固定页框;
页 3:
存在,
页 4:
存在,
页 2:
不存在。
现在访问:
02A01H。(Any Docs)
拆地址:
所以访问页 2。
页 2 不在内存:
缺页。
改进 CLOCK 经过扫描后优先让页 3 成为:
因此淘汰页 3。
页 3 原页框:
所以页 2 装入 60H。
物理地址:
最重要的坑是:
页 2 页表项里即使残留了一个旧页框号,只要存在位为 0,这个页框号就不能直接用于地址转换。
三十九、FIFO、LRU、CLOCK 怎么做得最快?
模拟题中有一道非常适合练“读表判断”:
给出每页:
装入时间;
最近访问时间;
访问位;
修改位。
然后同时问 FIFO、LRU、改进 CLOCK 淘汰谁。(Any Docs)
只需要看对应字段:
| 算法 | 看哪个字段 |
|---|---|
| FIFO | 装入时间最小 |
| LRU | 最近访问时间最小 |
| 改进 CLOCK | A、M,并结合指针扫描顺序 |
千万不要拿“最近访问时间”做 FIFO。
也不要拿“装入时间”做 LRU。
四十、题型八:工作集
工作集:
某一段最近的访问窗口内,进程访问过的不同页面集合。
设窗口大小为 。
工作集:
就是时刻 前最近 次/时间单位涉及的不同页面。
【2016 408 真题】
访问序列在 t 前最后 6 次为:
6,0,3,2,3,2。
窗口大小 6。
去重:
注意:
窗口 6 ≠ 工作集大小 6。
窗口内有重复页。
因此:
而不是 6。(Any Docs)
四十一、抖动 Thrashing
这是概念题非常爱考的。
抖动:
CPU 大量时间都在处理缺页和换页,真正执行程序的时间很少。
常见原因:
进程太多:
每个进程得到的页框太少。
工作集装不下。
于是:
A 缺页
→ 换 B
→ 马上访问 B
→ 又换 A
→ 又要 A……
磁盘疯狂 I/O。
【2011 408 真题】
题目给三个方案:
撤销部分进程;
增加交换区;
提高进程优先级。
有效措施是:
因为可以减少多道程序度,让剩余进程得到更多页框。
单纯扩大 swap:
并不能让进程的工作集装进物理内存。
提高优先级也不能增加它的驻留集。(Any Docs)
四十二、工作集与抖动本质联系
如果进程 当前工作集大小:
所有进程总工作集需求:
如果物理页框总数为 。
当:
说明系统无法同时容纳所有活跃进程当前真正需要的页面。
于是很容易:
所以解决思路:
降低:
最直接就是减少活跃进程数。
四十三、题型九:TLB
TLB 可以理解成:
页表的 Cache。
页表在内存中。
如果每次访问数据都:
先查页表
→ 再访问数据,
一次逻辑访存变成两次物理访存。
所以把近期常用的:
缓存到高速 TLB。
四十四、没有 TLB 与有 TLB
设:
TLB 查询:
内存:
一级页表。
如果 TLB 命中:
如果 TLB 未命中,并且 TLB 与内存页表不是并行访问:
平均时间:
其中 是 TLB 命中率。
四十五、模拟题:反推 TLB 命中率
本站有一道很好的模拟题:
TLB 查询 1ns;
内存访问 100ns;
TLB 与页表查询并行;
要求开启分页以后平均访存时间不超过 110ns。
求最低 TLB 命中率。(Any Docs)
命中:
TLB 1ns 得到结果,然后访问数据:
未命中。
因为 TLB 与内存中的页表同时开始查:
100ns 时页表完成,
然后再访问一次目标数据:
设命中率 :
整理:
这种题最关键的不是公式背诵,而是先判断:
TLB 和页表是串行查,还是并行查?
题目条件不同,公式就不同。
四十六、超级经典:2009 TLB + 缺页 + LRU 综合大题
这是第 3 章非常值得反复做的一道题。
【2009 408 真题】
页大小:
4KB。
页表部分情况:
页 0 → 101H,有效;
页 1 → 不在内存;
页 2 → 254H,有效。
TLB 初始为空。
TLB:
10ns。
内存:
100ns。
一次缺页:
驻留集固定为 2;
使用 LRU。
访问:
2362H
→ 1565H
→ 25A5H。(Any Docs)
第一次:2362H
访问页 2。
页 2 在内存,但 TLB 空。
所以:
查 TLB:
10ns;
TLB miss;
查内存页表:
100ns;
访问目标数据:
100ns。
总时间:
同时页 2 进入 TLB。
第二次:1565H
访问页 1。
TLB miss:
10ns。
查页表:
100ns。
发现存在位 0。
到这一刻:
产生缺页。
缺页处理:
因为驻留集固定为 2:
当前页 0 和页 2 在内存。
刚刚访问过页 2。
LRU 淘汰:
页 1 被装入页 0 原来的页框:
处理完成以后:
重新执行发生缺页的指令。
题目说明缺页处理已经更新 TLB。
于是重执行:
TLB 命中:
总计:
应从这个数字直观感受到:
一次缺页的代价比普通内存访问大好几个数量级。
第三次:25A5H
页 2。
页 2 仍在内存,并且 TLB 已经缓存映射。
所以:
页 1 的最终物理地址
页 1 被装入:
页框。
所以:
这道题一定要掌握什么?
它实际上把五个点串在了一起:
TLB
→ 页表
→ 缺页
→ LRU
→ 物理地址。
以后看到复杂大题就按这个顺序拆开,而不是试图一口气想完。
四十七、有效访存时间 + 缺页率
设正常访存时间:
缺页率:
一次缺页处理的额外代价:
则近似:
因为:
很多题近似写成:
四十八、模拟题:反推最大缺页率
本站有一道很有价值的模拟题:
正常内存访问 1μs;
未修改页缺页处理 1ms;
修改页缺页处理 10ms;
60% 被换出的页面修改过;
要求 EAT 不超过 3μs。(Any Docs)
平均缺页处理时间:
转成 μs:
正常分页访问约为:
于是:
因此:
这类题透露出一个非常重要的考试直觉:
允许的缺页率往往必须极低。
因为磁盘 I/O 太慢。
四十九、为什么修改位这么重要?
假设替换一个干净页面。
磁盘里已经有正确副本。
可以直接:
丢掉。
但修改页:
内存是最新版本。
所以必须:
于是一次置换可能发生:
两次磁盘 I/O。
所以所有置换算法都倾向于:
在条件相近时优先淘汰干净页。
五十、题型十:缺页次数和页面置换次数不能混
【2019 408 真题】
进程预分配 4 个页框,LRU,访问:
0,1,2,7,0,5,3,5,0,2,7,6。
问:
页面置换次数,不是缺页次数。(Any Docs)
前四个页面:
0,1,2,7
都是缺页。
但是页框还空着。
所以:
之后访问新页才开始淘汰。
最终置换:
5 次。
所以必须区分:
开始填充空页框时:
有缺页,没有置换。
五十一、题型十一:页面缓冲算法
本站模拟题专门补充了页面缓冲。(Any Docs)
核心思想:
被淘汰的页不要立刻彻底处理掉。
例如干净页放进:
空闲页面链。
脏页放进:
修改页面链。
如果刚淘汰不久又访问它,而且原页框还没被重新利用:
就可以快速拿回来。
这利用的仍是:
一个很容易出的陷阱
页面缓冲可以:
减少实际磁盘 I/O;
缩短缺页处理代价。
但:
不一定改变“逻辑上有没有发生缺页”。
2022 真题就从另一角度考:
页面缓冲队列长度不会直接决定缺页率,而页面置换算法、工作集、系统进程数量都可能影响缺页率。(Any Docs)
一定区分:
与:
这是两个不同指标。
五十二、2012 408 大题:页面缓冲 + 时间局部性
这道真题给出:
每隔 5 个时间单位扫描驻留集;
本轮没访问的页面回收到空闲页框链尾;
但页框内容暂时不清空;
如果以后又访问且原内容还在,就重新拿回来。(Any Docs)
例如页 1 原来占页框 32。
后来因为一个扫描窗口内没访问,被回收到空闲链。
但内容仍然是页 1。
随后很快再次访问页 1。
那么系统发现:
32 号页框里的旧内容还没有被覆盖。
可以直接恢复:
号页框。
不需要磁盘重新读。
所以这种策略特别适合:
五十三、题型十二:程序局部性
这是这几年特别值得重视的交叉知识点。
局部性分:
时间局部性:
刚访问过的东西很可能很快再次访问。
空间局部性:
访问某地址以后,很可能继续访问附近地址。
分页、TLB、Cache 都大量依赖局部性。
五十四、数组按行还是按列?
【2020 408 大题】
数组:
a[1024][1024]每个元素 4B。
起始虚拟地址:
10800000H。
题目问:
a[1][2] 的虚拟地址;
页目录号、页号;
页目录项和页表项地址;
虚拟空间/物理空间是否连续;
最后还问按行遍历和按列遍历哪个局部性好。(Any Docs)
a[1][2] 地址
C 语言二维数组行优先。
前面经过:
个元素。
每个 4B:
所以虚拟地址:
五十五、二维数组地址通式
对于:
A[M][N]元素大小 。
0 下标时:
如果题目用 1 下标,要特别修改。
五十六、为什么按行遍历好?
数组按行存储。
同一行:
A[i][0], A[i][1], A[i][2]...
地址连续。
所以按行访问:
而按列:
A[0][j]
→ A[1][j]
→ A[2][j]
每次跨越一整行。
这道 2020 真题中:
一行大小:
恰好一页。
所以按列每访问下一元素:
几乎就跳到下一页。
极易:
TLB miss
-
Cache miss
-
甚至缺页。
而按行可以连续吃完整个页面。(Any Docs)
五十七、模拟题把这一点考得更极端
【王道·卷二 Q29】
数组:
128×128。
一页正好放 128 个数组元素。
进程工作集只有两个页框。
代码占 1 个。
数据事实上只剩:
个页框。
程序 1 按列扫描;
程序 2 按行扫描。(Any Docs)
按列:
每访问一个元素都跳到另一页。
大约:
次缺页。
按行:
一行正好一页。
每行只第一次访问缺页:
次。
两者差距:
这就是“代码写法本身影响虚拟内存性能”的经典考法。
五十八、虚拟地址连续 ≠ 物理地址连续
仍然是 2020 真题。
数组在 C 程序中:
逻辑上连续。
所以它在虚拟地址空间需要连续。
但相邻虚页可以:
页 100 → frame 8
页 101 → frame 999
页 102 → frame 17。
所以物理页框完全可以离散。
因此:
这恰恰是分页的重要价值。
五十九、虚拟内存容量到底由什么决定?
这是非常高频的错误说法。
有人说:
虚拟内存大小 = 内存 + 硬盘。
不严谨。
理论虚拟地址空间首先受:
约束。
例如 32 位虚拟地址:
地址空间。
2023 真题直接考过:
虚拟地址空间大小不是由“物理内存 + 硬盘大小”决定。(Any Docs)
所以:
swap 只是虚拟内存实现中的后备存储资源之一。
六十、一道非常容易忽略的题:最少要几个页框?
模拟题:
指令系统允许一级间接寻址;
请求分页;
问一个进程至少需要多少页框才能保证指令完成。(Any Docs)
最坏情况:
一条指令本身跨两个页面:
页。
一级间接寻址的间接地址:
可能另占:
页。
最终操作数:
又在另一个页面:
页。
所以至少:
个页框。
这种题不是普通地址计算,而是在问:
一条指令执行过程中最多需要同时存在多少个页面?
属于很好的拓展题型。
六十一、多级页表综合题怎么拆?
以后遇到:
32 位地址:
只要看到虚拟地址:
0xXXXXXXXX
立刻拆:
页目录:
二级页表索引:
offset:
六十二、页目录项地址怎么算?
页目录基址:
页目录号:
每项 4B。
那么:
查 PDE 得到二级页表页框:
于是二级页表基址:
页表索引:
所以:
再查 PTE:
得到数据页框 。
最终:
这套链条一定要非常熟:
六十三、整个第 3 章综合大题统一做题模板
以后不管题面多长,都按下面这个顺序。
| 步骤 | 做什么 |
|---|---|
| 1 | 先确定页面大小,得到 offset 位数 |
| 2 | 把虚拟地址拆成页号/各级索引/offset |
| 3 | 先查 TLB,判断 hit / miss |
| 4 | TLB miss 再按层级查页表 |
| 5 | 检查存在位、权限、越界等控制位 |
| 6 | 缺页则判断有没有空页框 |
| 7 | 没空页框时应用 FIFO/LRU/CLOCK 等 |
| 8 | 脏页需要写回,随后调入新页 |
| 9 | 更新页表、TLB、相关状态位 |
| 10 | 重新执行发生缺页的指令 |
| 11 | 得到页框号后拼接 offset |
| 12 | 如果问时间,再把各次 TLB/内存/磁盘访问逐项相加 |
看到复杂题不要慌。
本质上永远只是这条流水线。
六十四、几个特别容易丢分的“不是一回事”
| 概念 A | 概念 B | 区别 |
|---|---|---|
| 页 page | 页框 frame | 虚拟 vs 物理 |
| 缺页 | 页面置换 | 有空页框时缺页但不置换 |
| TLB miss | 缺页 | TLB 没有不代表内存没有 |
| 缺页 | 越界 | 合法但不驻留 vs 地址非法 |
| 页号 | 页框号 | 虚拟编号 vs 物理编号 |
| 虚拟地址连续 | 物理地址连续 | 前者完全可以对应离散页框 |
| FIFO | LRU | 装入时间 vs 最近访问时间 |
| 缺页率 | 缺页处理时间 | 发生概率 vs 一次处理代价 |
| 页表 | TLB | 完整映射结构 vs 高速缓存 |
| 虚拟地址空间 | swap | 地址范围 vs 外存后备空间 |
| 内部碎片 | 外部碎片 | 已分配内部浪费 vs 空闲空间零散 |
这张表建议反复看。
六十五、TLB miss、page fault 的完整关系
这也是经常理解错的地方。
情况 A:TLB hit
TLB:
有映射。
直接得到 frame。
通常不查内存页表。
情况 B:TLB miss,但页表有效
TLB 没缓存。
但页面其实在物理内存。
于是:
TLB miss
→ 查页表
→ 得到 frame
→ 更新 TLB
→ 访问数据。
没有缺页。
情况 C:TLB miss,页表存在位 0
TLB miss
→ 查页表
→ 发现不驻留
→ page fault。
所以关系:
但在一般设计下发现 page fault 前通常要查到相关页表状态。
六十六、页面大小变大,到底有什么影响?
这是很适合出综合选择题的知识点。
页面变大:
页数减少。
因此:
页表更小。
TLB 每个表项覆盖范围扩大。
磁盘顺序传输效率可能提高。
但是:
内部碎片一般增大。
一次调入的数据更多。
如果空间局部性差,会装进很多无用数据。
所以没有:
“页面越大一定越好”
或:
“页面越小一定越好”。
408 最喜欢这种绝对化选项。
六十七、一个很实用的平均内部碎片结论
如果进程大小相对页面边界可以近似认为随机。
最后一页平均浪费:
其中 为页大小。
所以页面越大:
平均内部碎片一般越大。
这个虽然不一定每次直接考公式,但非常适合用来判断选项。
六十八、缺页异常属于什么?
缺页通常属于:
它由当前正在执行的指令访问虚拟地址引起。
磁盘完成页面读入以后产生的完成通知则属于:
一道综合题完全可能把第 2 章异常/中断和第 3 章虚拟内存结合起来。
六十九、为什么缺页后要重新执行指令?
假设执行:
x = A[i];CPU 执行到访问 A[i]:
发现页面不存在。
此时这条指令还没真正完成。
OS 调入页面以后不能直接执行下一条。
必须:
这也是计算一次缺页总时间时经常忘掉的一部分。
2009 那道综合真题就明确利用了这一点。
七十、数组题还可能和 Cache 联合
这是非常值得预测的一种 408 综合考法。
已经有真题把:
二维数组
-
分页
-
二级页表
-
局部性
放在一起。
后续完全可以继续加入:
Cache。
例如:
页面 4KB;
Cache block 64B;
数组元素 4B。
一个页面有:
个 int。
一个 Cache block 有:
个 int。
如果顺序遍历:
每 16 个 int 一次 Cache 块缺失;
每 1024 个 int 一次新页。
因此一个循环可以同时问:
Cache miss
-
TLB miss
-
page fault。
这种组合非常符合 408 “计组 + 操作系统交叉”的风格。
七十一、伙伴算法也可能和碎片一起升级
2024 已经明确考 Buddy。
下一步值得防的题:
给:
总内存 1MB;
最小块 4KB;
依次申请:
70KB、130KB、20KB……
要求写出实际分配块大小。
例如申请 70KB:
最近的 :
所以内部浪费:
这能把:
伙伴系统
-
内部碎片
-
地址对齐
组合起来。
七十二、还有一种值得防:Copy-on-Write
虽然传统基础教材不会把它当本章最核心内容,但它和近年的“共享物理页”题非常自然。
例如 fork 后:
父进程页
子进程页
一开始都映射:
于是:
完全可以,而:
当某个进程写页面时发生保护异常,OS:
复制物理页
→ 修改该进程页表
→ 以后两个进程页框不同。
这本质上就是 2023“共享数据页”考法的自然延伸。
七十三、从本站里的真题分布来看,应该怎么复习?
本章不要平均用力。
最应该反复训练的是下面四条链。
第一条
这是基础。
第二条
这是多级页表。
第三条
这是综合大题。
第四条
这是性能分析题。
七十四、最后给出压缩成考场判断口诀
分区:
固定有内碎,动态有外碎;
Best Fit 小洞多;
回收先看前邻后邻;
紧凑需要动态重定位。
分页:
页号在高位,偏移在低位;
offset 位数决定页大小;
页表基址 + 页号 × PTE 大小;
物理地址 = 页框 × 页大小 + offset。
多级页表:
上一级 PTE 指向下一级页表的物理页框;
多级页表省页表空间,不是为了加快访问;
没有 TLB,级数越多访存次数通常越多;
PDBR 指向最高级页表的物理地址。
分段:
先查段号;
再查 offset 是否越过段长;
再看权限;
最后才是 base + offset。
虚拟内存:
缺页是“合法但不在内存”;
有空页框不一定置换;
脏页换出要写回;
缺页后重新执行原指令。
置换算法:
FIFO 看谁最早进;
LRU 看谁最久没用;
CLOCK 看 A;
改进 CLOCK 看 A、M;
FIFO 可能 Belady。
共享:
虚页号可以不同;
共享物理页框必须相同。
局部性:
C 二维数组按行存;
按行遍历通常远好于按列。
本章最应该真正做会的真题
如果时间有限,第 3 章建议至少把这些题真正拿纸完整算一遍:**2013 分页/二级页表大题、2009 TLB+缺页+LRU 大题、2010 FIFO/CLOCK 大题、2012 页面缓冲大题、2015 多级页表大题、2018 PDBR/地址组成大题、2020 二维数组+二级页表大题,再配 2021 改进 CLOCK 和 2023 共享页选择题。**它们基本覆盖了内存管理最重要的命题骨架。本站中的真题也确实持续围绕这些主题反复变化。(Any Docs)
如果只记这一章最重要的一句话,就是:
这条思路真正熟练以后,第 3 章那些看起来很长的综合大题,实际上都只是把几个固定步骤串起来。