第 3 章 存储系统
408 计算机组成原理第 3 章 存储系统复习讲义,涵盖核心概念、重点考点与典型题型。
Last updated on
先建立整章的核心逻辑:
CPU 很快,但又快又大又便宜的存储器不存在。
所以计算机采用存储层次:
寄存器 → Cache → 主存 → SSD/磁盘
Cache 主要解决速度矛盾;虚拟存储器主要解决容量矛盾;二者能够成立的共同基础都是程序访问的局部性。
一、第 3 章考点地图
| 内容 | 考研重要度 | 主要题型 |
|---|---|---|
| 存储器分类、层次结构 | ★★★ | 概念选择 |
| MAR、MDR、地址线、数据线 | ★★★★ | 地址范围、位数计算 |
| SRAM / DRAM | ★★★★ | 特点辨析、地址引脚、刷新 |
| DRAM 行列地址、行缓冲 | ★★★★ | 计算、选择 |
| 多体低位交叉存储 | ★★★★ | 带宽、体号、体冲突 |
| 主存芯片位扩展、字扩展 | ★★★★ | 芯片数量、地址范围 |
| 磁盘 | ★★★ | 容量、存取时间 |
| SSD | ★★ | 原理辨析 |
| Cache 基本原理、命中率 | ★★★★★ | 计算 |
| Cache 三种映射 | ★★★★★ | 地址划分、映射、综合题 |
| Cache 总容量 | ★★★★★ | 高频计算 |
| LRU / FIFO | ★★★★ | 模拟替换 |
| Cache 写策略 | ★★★★ | 概念辨析 |
| 页式虚拟存储器 | ★★★★★ | 地址转换、缺页 |
| TLB | ★★★★★ | 地址划分、映射 |
| TLB + 页表 + Cache | ★★★★★ | 综合题 |
| 段式、段页式 | ★★★ | 原理比较 |
资料中的考点追踪也与这个分布非常一致:组相联映射、虚实地址转换、分页缺页、TLB 等多年反复出现。
二、存储器概述:为什么需要存储层次?
2.1 一个存储器有三个互相矛盾的要求
我们希望存储器同时:
- 速度特别快;
- 容量特别大;
- 每 bit 特别便宜。
但现实中做不到。
一般越靠近 CPU:
| 层次 | 速度 | 容量 | 每 bit 成本 |
|---|---|---|---|
| 寄存器 | 极快 | 极小 | 极高 |
| Cache | 很快 | 小 | 高 |
| 主存 | 中等 | 较大 | 中等 |
| SSD / 磁盘 | 慢 | 很大 | 低 |
所以计算机采用多级结构。资料概括得非常重要:
Cache—主存层主要解决 CPU 与主存的速度不匹配,而且调度主要由硬件自动完成。
主存—辅存层主要解决主存容量不足,通过操作系统与硬件共同完成,从而形成虚拟存储器。
而且上层内容本质上是下层内容的一部分副本:
Cache 中的数据来自主存;主存中的活跃内容又来自辅存。
这句话后面理解 Cache、页表和虚拟存储器时非常重要。
三、主存储器最基本的工作过程
这是看起来简单,但后面总线、CPU、Cache 都会继续使用的一套逻辑。
CPU 要读某个地址的数据时:
CPU
│
│ 地址
▼
MAR
│
│ 地址总线
▼
主存地址译码器
│
▼
选中某个存储单元
│
│ 数据总线
▼
MDR
│
▼
CPU写操作则反过来:
CPU → MDR → 数据总线 → 主存资料中特别强调:
MAR 位数主要对应可寻址地址空间,MDR 位数对应一次可传送的数据宽度。
例如按字节编址,有 36 位地址:
所以其地址范围为:
如果数据总线是 64 位,则一次能够并行传送:
一个非常常见的混淆
地址线决定“有多少个地址”。
数据线决定“一次能传多少数据”。
不要把二者混起来。
四、存取时间、存取周期、带宽
这几个概念选择题很喜欢互换。
4.1 存取时间
一次读或写真正需要的时间。
比如主存收到有效地址开始,到读出的数据稳定输出为止。
4.2 存取周期
两次独立存储器访问之间允许的最小时间间隔。
一般:
为什么可能大于?
因为读完之后存储器内部还可能需要恢复、再生、刷新或稳定。
所以:
存取时间关注“一次访问多长”;
存取周期关注“多久才能再开始下一次独立访问”。
4.3 存储器带宽
表示单位时间能够传输多少数据。
它不仅取决于存储器本身速度,还与一次能够传多少 bit、总线宽度等有关。
五、SRAM 和 DRAM
这是主存硬件部分最重要的基础。
资料中的核心对比如下。
| 特点 | SRAM | DRAM |
|---|---|---|
| 存储元件 | 触发器 | 电容 |
| 是否刷新 | 不需要 | 需要 |
| 读出 | 非破坏性 | 破坏性 |
| 速度 | 快 | 较慢 |
| 集成度 | 低 | 高 |
| 成本 | 高 | 低 |
| 常见用途 | Cache | 主存 |
可以直接从物理结构理解。
5.1 SRAM 为什么快?
SRAM 用类似触发器的稳定状态保存 0、1。
只要不断电,状态就能维持,因此:
不需要刷新;
读一次也不会把信息破坏掉。
缺点是一个 bit 要用较多晶体管,所以面积大、价格高。
因此特别适合:
容量不需要特别大,但一定要快的 Cache。
六、DRAM:这一部分一定要理解
6.1 DRAM 为什么要刷新?
DRAM 用电容是否存有电荷来表示 0 和 1。
但现实中的电容会漏电。
所以即使 CPU 什么都不做,过一段时间,原来的“1”也可能因为电荷泄漏变成无法识别的状态。
因此 DRAM 必须:
定期读出来,再重新写进去。
这就是刷新。
而且 DRAM 本身属于破坏性读出:
一读,原来的电荷状态会受到破坏,所以普通读取之后也必须进行再生。
七、DRAM 地址引脚复用——高频
这是很经典的计算题。
假设一个 DRAM 芯片有:
其中:
意味着需要区分:
个存储位置。
如果像普通地址一样一次把完整地址送进去,就需要 22 根地址线。
但 DRAM 为了减少芯片引脚,把地址拆成:
行地址
+
列地址例如:
形成:
的二维阵列。
所以只需要 11 根物理地址引脚。
第一次:
11根引脚 → 行地址
↓
RAS第二次:
同样11根引脚 → 列地址
↓
CAS也就是说:
22 位逻辑地址没有消失,只是利用相同的 11 根引脚分两次传送。
这叫做行、列地址复用。
八、DRAM 为什么设计成接近正方形?
假设存储阵列容量为:
其中行数为 ,列数为 ,则:
行地址位数:
列地址位数:
因为行列共用地址引脚,所以真正需要的地址引脚数量取:
为了让这个最大值尽量小,就应该让:
也就是阵列尽可能接近正方形。
但还有第二个条件:
DRAM 刷新是按行刷新。
行越多,需要刷新的行越多。
所以资料指出通常希望:
于是最佳思路就是:
行列尽量接近,同时行数不要比列数更多。
这是 2018 年考点追踪中的内容。
九、DRAM 刷新到底刷新什么?
这是很容易想错的地方。
例如:
不是要执行:
次刷新。
而只需要:
次。
因为一次刷新一整行。
也就是:
给出行地址
↓
选中整行
↓
整行读出并再生刷新时甚至不需要列地址。
十、DRAM 行缓冲器与突发传输
这一部分和现代内存为什么连续读很快直接相关。
假设一个 DRAM:
每个单元:
当选中一行时,并不是只把一个单元送出来,而是把这一整行加载到行缓冲器。
因此行缓冲器容量为:
一般公式:
一旦行已经打开,后续访问同一行的相邻列就快得多,因此可以连续输出数据,这就是突发传输的重要基础。
可以把 DRAM 想成:
第一次打开一本书到某一页比较慢; 但这一页已经摊开后,连续读取这一页上的内容就非常快。
十一、多模块存储器与低位交叉——高频计算
CPU 比单个存储体快得多。
一个存储体刚开始一次访问后,在接下来的存取周期里暂时不能再次接受新请求。
怎么办?
答案是:
准备多个存储体,轮流访问。
11.1 高位连续编址
例如有 4 个体:
M0:0 ~ n-1
M1:n ~ 2n-1
M2:2n ~ 3n-1
M3:3n ~ 4n-1连续访问:
0,1,2,3,4,...前面大量地址都落在 M0。
所以还是不断访问同一个体,无法充分并行。
资料指出这种连续编址本质上仍是顺序访问。
十二、低位交叉编址
现在换一种方式:
地址0 → M0
地址1 → M1
地址2 → M2
地址3 → M3
地址4 → M0
地址5 → M1
...对于 个存储体:
所以连续地址会均匀分布到不同存储体。
这样:
t=0 M0 开始工作
t=r M1 开始工作
t=2r M2 开始工作
t=3r M3 开始工作
...就像流水线。
12.1 最重要公式
如果单个存储体存取周期为 ,相邻存储体启动间隔为 ,为了实现无冲突轮流启动,需要:
12.2 资料中的典型题
八体低位交叉存储器:
单体存储周期:
所以启动间隔:
一次传 64 位:
因此稳定后:
得到:
也可以直接:
其中 是每个体一次输出的数据量。资料配套题答案也是 800 MB/s。
注意
低位交叉主要提高的是:
连续访问时的吞吐率 / 带宽。
它并没有 magically 把单个存储体本身的存取周期变短。
十三、体冲突怎么做?
体冲突题不要凭感觉。
先算:
然后逐个模拟请求启动。
若某个体在一个完整存取周期内再次被访问:
该请求必须等待。
如果题目明确说“发生冲突以后后续请求整体延迟”,则后面的启动时间也跟着往后移动。
所以这类题本质不是难算法,而是:
求模 + 时间轴模拟。
十四、主存芯片扩展——一定要会
主存扩展有三个概念:
位扩展、字扩展、字位扩展。
最简单的判断方法:
“宽度不够” → 位扩展。 “地址数量不够” → 字扩展。 两个都不够 → 字位扩展。
十五、位扩展
例如:
芯片:8K × 1 bit
目标:8K × 8 bit地址数量已经足够:
8K → 8K但是一个芯片一次只有 1 bit,需要 8 bit。
所以:
片。
连接关系:
A0~A12 ─┬→ 芯片0
├→ 芯片1
├→ ...
└→ 芯片7
所有芯片同时选中
芯片0 → D0
芯片1 → D1
...
芯片7 → D7所有芯片:
- 地址线相同;
- 片选相同;
- 读写控制相同;
- 数据线分别占一个 bit。
所以是并行工作。
十六、字扩展
例如:
芯片:16K × 8 bit
目标:64K × 8 bit位宽已经够了。
但地址数量差:
所以需要 4 片。
单片:
因此芯片内部地址需要:
整个 64K:
所以总地址:
高出的:
拿去做片选。
于是:
| A15 A14 | 芯片 | 地址范围 |
|---|---|---|
| 00 | 0 | 0000H~3FFFH |
| 01 | 1 | 4000H~7FFFH |
| 10 | 2 | 8000H~BFFFH |
| 11 | 3 | C000H~FFFFH |
这正是资料中的例子。
十七、字位扩展的万能计算法
这是我根据资料中的位扩展和字扩展规则整理出的通用解题模板。
目标:
M × W单片:
m × w位扩展倍数:
字扩展倍数:
总芯片数:
例如:
目标:64K × 16
芯片:16K × 8则:
所以:
可以理解成:
一组2片并联 → 构成16K×16
一共4组 → 构成64K×16十八、磁盘存储器
磁盘不是本章最难,但计算公式非常固定。
磁盘要先理解:
一个盘片
├─ 上表面
└─ 下表面
每个表面
└─ 一圈圈磁道
磁道
└─ 划分为扇区不同盘面相同半径位置的一组磁道组成柱面。
因此磁盘地址通常表示为:
柱面号 + 磁头/盘面号 + 扇区号18.1 格式化容量
最常考:
资料也指出:
因为格式化需要同步信息、间隙等额外空间。
十九、磁盘存取时间——高频公式
磁盘真正读到数据之前,要做三件事:
1. 磁头移动到目标磁道
↓
2. 等待目标扇区转到磁头下面
↓
3. 真正传输数据所以:
资料将这三部分称为磁盘核心存取时间;若算完整响应时间,还可能加入排队延迟和控制器时间。
19.1 平均旋转等待时间
假设转速为 RPM。
转一圈时间:
平均等待半圈:
例如 7200 RPM:
平均等待:
这就是为什么机械磁盘随机访问很慢:不是磁介质本身传数据慢,而是机械寻道 + 等待旋转很耗时间。
二十、SSD
资料中的 SSD 基于 Flash,由一个或多个闪存芯片以及闪存翻译层等组成,不存在机械磁头与盘片。
所以最直观的区别是:
HDD:
寻道 → 等旋转 → 传输
SSD:
电子方式定位 → 访问Flash因此 SSD 的随机访问性能远好于机械磁盘。
这一节考研中更多是理解和选择题,不需要像 Cache 那样投入大量精力。
二十一、Cache:本章第一核心
现在进入第 3 章最重要部分之一。
Cache 的逻辑其实只有一句:
把 CPU 最近很可能还要访问的主存块复制一份放在高速 SRAM 中。
依据就是局部性原理。
二十二、时间局部性与空间局部性
时间局部性
一个数据刚访问过,那么短时间内很可能还会再次访问。
例如:
for (...) {
sum += a;
}变量 sum、循环控制指令会不断重复访问。
空间局部性
刚访问地址 X,那么接下来很可能访问:
X+1
X+2
X+3
...例如顺序访问数组。
资料特别通过二维数组按行、按列访问说明:即使两个程序实现完全相同的功能,因为内外循环顺序不同,也可能产生完全不同的 Cache 命中情况。
这也是为什么:
for (i)
for (j)
a[i][j];通常比按列跳着访问更符合 C 语言二维数组的空间局部性。
二十三、Cache 为什么按“块”交换?
Cache 不会只从主存拿你当前需要的一个字节。
而是把它所在的整个主存块搬进 Cache。
例如:
一个 Cache 块 = 64BCPU 要地址 1000。
那么可能直接搬:
地址 960 ~ 1023这一整块。
因为根据空间局部性:
既然访问 1000,很可能马上还会访问 1001、1002……
这就是“块”的意义。
二十四、Cache 命中与缺失
CPU 每次访存先查 Cache:
CPU给地址
↓
查 Cache
┌─────────────┐
│ │
命中 未命中
│ │
直接读取 访问主存
↓
整块调入Cache
↓
再交给CPU这个过程由硬件自动完成,对程序员透明。
二十五、Cache 命中率计算
假设命中次数为 ,未命中次数为 ,则:
缺失率:
如果 Cache 访问时间为 ,主存调块损失为 ,且采用资料所说的“先查 Cache,缺失再访问主存”,则:
整理:
这是非常重要的公式。
25.1 资料中的典型例子
Cache 比主存快 5 倍:
命中率:
于是:
不用 Cache 需要:
所以性能变为:
即约提升到原来的 4 倍。
做题特别注意
一定看题目:
Cache 和主存是先后访问还是同时访问。
平均时间公式会因此不同。资料中的主要模型是先 Cache、后主存。
二十六、Cache 映射:整个第 3 章最重要计算之一
先记住三种:
直接映射
全相联映射
组相联映射难点实际上不是名字,而是:
一个主存块到底允许放到 Cache 的哪些位置?
二十七、直接映射
直接映射规定:
每个主存块只能进入唯一一个 Cache 行。
假设 Cache 有 行:
例如 Cache 有 8 行。
主存块:
0 → 行0
1 → 行1
...
7 → 行7
8 → 行0
9 → 行1
...因此块 0、8、16、24……都抢 Cache 行 0。
优点:
简单、快、便宜。
缺点:
很容易发生冲突。
二十八、直接映射的地址结构
若按字节编址:
主存地址
┌──────────┬──────────┬────────────┐
│ Tag │ Cache行号 │ 块内偏移 │
└──────────┴──────────┴────────────┘假设:
- 主存地址为 位;
- Cache 数据容量为 字节;
- 块长为 字节。
Cache 行数:
块内偏移位数:
Cache 行号位数:
Tag 位数:
这三个公式一定要熟练。
二十九、为什么需要 Tag?
假设:
块0 → 行0
块8 → 行0
块16 → 行0CPU 只看到“行 0”是不够的。
必须知道行 0 当前到底装的是谁。
所以 Cache 行中除了真正的数据,还有:
Tag
有效位
数据块Tag 就是:
“我现在存的是哪一个主存块?”
三十、资料中的 Cache 容量经典例题
主存:
按字节编址,所以物理地址:
Cache:
8 行
每行 64B
直接映射块内偏移:
所以:
8 个 Cache 行:
所以行号:
Tag:
于是地址结构:
┌───────────┬───────┬────────┐
│ Tag 19bit │ 行 3bit│偏移6bit │
└───────────┴───────┴────────┘三十一、Cache “数据容量”和“总容量”不是一回事
每行真正的数据:
但还要保存:
- 19 位 Tag;
- 1 位有效位。
因此每行实际需要:
8 行:
所以:
而纯数据容量只是:
这是 Cache 题最容易丢分的地方之一。
如果题目再给出
- 脏位;
- LRU 位;
- 其他控制位;
也要一并加进去。
所以看到“Cache 总容量”四个字,第一反应应该是:
不能只算数据区。
三十二、直接映射主存块号怎么算?
假设访问字节地址:
块大小:
主存块号:
Cache 8 行:
因此去 Cache 第 2 行。
这是整个 Cache 地址题最基础的三步:
然后:
最后:
三十三、全相联映射
全相联相反:
一个主存块可以进入 Cache 中任意一行。
地址就不需要行号字段:
┌───────────────────┬──────────┐
│ Tag │ 块内地址 │
└───────────────────┴──────────┘优点:
几乎不存在映射位置造成的冲突。
问题:
CPU 怎么知道它在哪一行?
答案只能是:
把主存块 Tag 和 Cache 里所有行的 Tag 同时比较。
所以如果 Cache 有 1024 行,就需要大量比较电路。
因此:
命中率高,但硬件复杂、查找成本高。
三十四、组相联映射——最重要
这是现代考试最喜欢出的。
组相联是:
组间直接映射,组内全相联。
例如 Cache:
8 行
2 路组相联每组 2 行。
所以:
主存块先确定唯一的组:
进入该组以后:
可以放在该组任何一路。
34.1 仍用块号 50
4 组:
所以属于:
第 2 组。
如果每组对应连续的两行,则可进入:
第4行
或
第5行资料的例子就是这个结果。
三十五、组相联地址结构
┌──────────┬────────┬────────────┐
│ Tag │ 组号 │ 块内偏移 │
└──────────┴────────┴────────────┘假设:
- Cache 数据区容量为 ;
- 块长为 ;
- 路组相联。
Cache 行数:
组数:
组号位数:
块内偏移:
Tag:
这是组相联大题最核心的公式组。
三十六、三种映射怎么比较?
资料给出的整体规律是:
| 特征 | 直接映射 | 组相联 | 全相联 |
|---|---|---|---|
| 放置自由度 | 最低 | 中间 | 最高 |
| 冲突概率 | 高 | 中 | 低 |
| 命中率 | 较低 | 中 | 较高 |
| 比较器开销 | 低 | 中 | 高 |
| 硬件复杂度 | 低 | 中 | 高 |
| 访问速度 | 快 | 中 | 较慢 |
不要死记。
从“一个块允许放几个地方”自然就能推出:
允许位置越多
→ 冲突越少
→ 命中率越高
→ 查找越复杂
→ 硬件越贵三十七、组相联的完整访问过程
资料明确将这个过程列为 2020 年考点。
CPU 给物理地址:
Tag | 组号 | 块内地址第一步,根据组号选中一个 Cache 组。
第二步,把地址中的 Tag 与这一组所有路的 Tag 并行比较。
第三步,如果:
Tag相同
且
有效位 = 1则命中。
第四步,根据块内偏移选择块中的具体字节/字。
第五步,如果均未匹配,则 Cache 缺失。
第六步,把对应主存块调进这个组。如果组已满,则执行替换算法。
三十八、比较器数量也是考点
直接映射:
已经通过行号唯一确定行,所以只需要比较一个 Tag。
路组相联:
一组有 行,需要同时比较 个 Tag。
所以:
比较器位宽一般对应 Tag 位数。
资料将其列为 2022 年考点。
三十九、Cache 替换算法
直接映射根本没有选择:
只有唯一一个位置,因此新块来了就覆盖。
所以:
直接映射不需要替换算法。
替换算法主要发生在:
- 全相联;
- 组相联。
资料重点给出 RAND、FIFO、LRU。
四十、FIFO
原则:
谁最早装进来,先淘汰谁。
它关心的是:
“什么时候进入 Cache。”
而不关心:
“最近有没有被访问。”
因此一个很热的数据即使刚刚又用了,也可能因为“进入得太早”而被 FIFO 淘汰。
四十一、LRU——重点
LRU:
Least Recently Used 最近最少使用
准确理解:
淘汰距离现在最久没有访问过的块。
例如 3 路 Cache 当前最近使用顺序:
A → 刚访问
C
B → 最久没访问新块 D 来时:
淘汰 B。
如果随后访问 C,则:
C → 最新
D
A → 最旧下一次就淘汰 A。
它利用了:
时间局部性。
资料中 LRU 的命中率分析在 2012、2021 出现,替换位相关内容在 2018、2020 出现。
41.1 LRU 做题最好不要死记计数器
手算题直接维护:
MRU ← 最近使用
...
LRU ← 最久未使用每次命中:
把该块提到最前。
每次缺失:
空闲则装入;满了就删最后一个。
比机械模拟硬件计数器稳得多。
资料中的硬件计数模型则使用每行的 LRU 状态记录相对新旧顺序,例如 4 路时每行计数值需要能够表达 4 种相对状态。
四十二、Cache 抖动
资料给出一个很典型的情形。
一个组只有 4 行,但循环访问 5 个映射到同一组的主存块:
1 2 3 4 5 1 2 3 4 5 ...LRU:
装1
装2
装3
装4
访问5 → 淘汰1
访问1 → 淘汰2
访问2 → 淘汰3
访问3 → 淘汰4
...结果:
几乎每次都缺失。
这就是典型的抖动。
四十三、Cache 写策略
读 Cache 比较简单。
写的问题在于:
Cache 是主存的副本。CPU 改了 Cache 后,主存怎么办?
因此有一致性问题。
四十四、写直达 Write Through
CPU 写 Cache 时:
Cache ← 新数据
主存 ← 同时写新数据优点:
Cache 与主存始终一致。
缺点:
每次写都访问主存,很慢。
因此可以加写缓冲区:
CPU → Cache
└──→ Write Buffer → 慢慢写主存资料特别指出写缓冲可以缓解速度差,但大量连续写时仍可能饱和。
四十五、回写 Write Back
CPU 写命中时:
只改 Cache
暂时不改主存等这个 Cache 块以后被替换时,再一次写回主存。
优点:
大幅减少主存写次数。
但问题来了:
怎么知道这个块究竟有没有改过?
所以需要:
Dirty Bit,脏位。
dirty = 0
→ Cache与主存一致
→ 替换时直接扔
dirty = 1
→ Cache被CPU修改过
→ 替换前必须写回主存所以:
Write Through 通常不需要 dirty bit;
Write Back 必须记录 dirty bit。
四十六、写不命中怎么办?
资料还区分:
Write Allocate
写不命中后,将相应主存块调入 Cache,再让其参与后续访问。
其思想是:
既然现在访问它,根据局部性,未来可能还会继续访问。
Not Write Allocate
写不命中:
直接写主存,不把这一块调入 Cache。
考选择题时一定把:
Write Through / Write Back与:
Write Allocate / Not Write Allocate看作两个不同维度的问题。
前者解决:
Cache 写命中后如何保持一致。
后者解决:
Cache 写不命中后要不要把这个块装入 Cache。
四十七、虚拟存储器:本章第二核心
Cache解决:
CPU 太快,主存太慢。
虚拟存储器解决:
程序需要的地址空间可能比物理内存更大。
例如:
程序看到:
0 ~ 4GB 虚拟空间
实际物理内存:
可能只有1GB程序并不要求全部同时进入主存。
只把当前真正需要的页放进去。
其他页面暂存在磁盘。
因此形成:
CPU
↓
虚拟地址
↓
地址转换
↓
物理地址
↓
主存四十八、页式虚拟存储器
把虚拟地址空间划成固定大小:
虚页。
把物理内存划成相同大小:
页框 / 物理页。
例如页大小:
那么任意地址最低:
就是页内偏移。
四十九、虚拟地址结构——必须一眼会拆
如果虚拟地址 位,页大小:
那么:
┌────────────────┬──────────────┐
│ 虚拟页号 n-p bit │ 页内偏移 p bit │
└────────────────┴──────────────┘其中:
例如:
32位虚拟地址
4KB页因为:
所以:
VPN:20 bit
Offset:12 bit五十、虚拟地址为什么不能直接访问内存?
因为虚页 5 不一定放在物理页框 5。
例如:
虚页0 → 页框7
虚页1 → 页框2
虚页2 → 不在内存
虚页3 → 页框9必须有一张表:
页表 Page Table
记录:
虚拟页号 → 物理页框号五十一、页表项有什么?
资料重点列出了:
| 字段 | 作用 |
|---|---|
| 物理页号 | 页面当前在哪个物理页框 |
| 有效位 | 当前是否在主存 |
| 脏位 | 是否被修改 |
| 引用位 | 是否最近使用过 |
有效位尤其重要:
Valid = 1
→ 页面在主存
Valid = 0
→ 页面不在主存
→ 缺页异常五十二、虚拟地址 → 物理地址全过程
资料把这一过程作为多年的高频考点。
虚拟地址:
VPN | Offset第一步:取出 VPN。
第二步:VPN 作为页表索引。
第三步:找到页表项。
第四步:检查有效位。
如果:
Valid=1得到物理页号 PPN。
第五步:
PPN | Offset拼接成为物理地址。
注意最重要的一点:
页内偏移不变!
即:
VA : VPN | Offset
↓↓↓↓↓↓
PA : PPN | Offset只有页号发生翻译。
五十三、为什么页内偏移不变?
因为:
一个虚页整体被放进某一个物理页框。
页内部数据之间的相对位置不会改变。
如果虚页中的第 123 个字节进入物理页框,那么它还是物理页框里的第 123 个字节。
因此:
这是地址转换题非常关键的检查条件。
五十四、缺页异常发生什么?
如果页表:
Valid = 0说明这一页不在内存。
过程大致为:
CPU访问
↓
发现页不在主存
↓
产生缺页异常
↓
操作系统介入
↓
从磁盘找到页面
↓
寻找空闲物理页框
↓
若没有空闲页框 → 页面置换
↓
必要时写回脏页
↓
新页调入主存
↓
修改页表
↓
重新执行相关访问资料明确指出缺页处理由操作系统完成,换出页面是否需要写回磁盘取决于脏位。
一个极重要区别
Cache Miss:
正常硬件事件。
Page Fault:
异常,需要操作系统介入,而且可能访问磁盘,代价极大。
五十五、为什么需要 TLB?
没有 TLB 时,一次普通访存:
CPU
↓
先访问主存页表
↓
得到物理页号
↓
再次访问主存
↓
真正的数据本来一次主存访问,现在至少变两次。
效率太低。
怎么办?
把最近使用的页表项放入一个高速小缓存:
TLB,Translation Lookaside Buffer
也就是快表。
五十六、TLB 到底缓存什么?
这是特别容易混淆的地方。
Cache 存:
主存中的数据块/指令块。
TLB 存:
页表项,即虚拟页号到物理页号的转换关系。
所以:
Cache = 数据的缓存
TLB = 地址翻译结果的缓存配套选择题也专门考过这个区别。
五十七、TLB 为什么采用相联存储?
CPU 拿到 VPN 时,希望:
一次快速判断“这个 VPN 在不在 TLB”。
因此 TLB 常采用:
- 全相联;
- 组相联。
全相联 TLB 中:
Tag 基本就是完整 VPN。
组相联中:
VPN
=
TLB Tag + TLB组号资料把 TLB 映射和地址字段分析列为 2016、2021 年考点。
五十八、TLB 命中与缺失千万别混淆
TLB Miss 只表示:
“翻译结果没缓存在 TLB。”
它不等于:
页面不在内存。
因此:
TLB Miss
↓
查主存页表
↓
┌──────────────┐
│ │
页表命中 页表无效
│ │
得到PPN Page Fault这是本章最容易混淆的一句话:
五十九、TLB + 页表 + Cache:综合题核心
这一块建议你以后遇到任何题都严格按照同一条流水线做。
假设 CPU 给出虚拟地址。
第一步:拆虚拟地址
VA
=
VPN | Page Offset其中:
第二步:查询 TLB
使用 VPN 查询 TLB。
TLB 命中
直接拿到 PPN。
TLB 缺失
访问主存中的页表。
第三步:查询页表
如果页表有效:
得到 PPN,同时可将页表项填入 TLB。
如果无效:
缺页异常 → 操作系统 → 磁盘。
第四步:形成物理地址
这里的符号表示拼接。
第五步:再用物理地址访问 Cache
如果 Cache 块长:
则低:
位为 Cache 块内偏移。
第六步:根据 Cache 映射方式取组号/行号
例如组相联:
第七步:Tag 比较
Tag匹配 + Valid=1
→ Cache Hit否则:
Cache Miss
→ 从主存调入块这整套流程就是资料特别强调的 “TLB + Cache 存储系统地址转换”。2024 年的考点追踪也出现了这一过程。
六十、TLB、Page、Cache 三种命中关系
资料专门总结过这张逻辑表。
| TLB | Page | Cache | 是否可能 |
|---|---|---|---|
| Hit | Hit | Hit | ✓ |
| Hit | Hit | Miss | ✓ |
| Miss | Hit | Hit | ✓ |
| Miss | Hit | Miss | ✓ |
| Miss | Miss | Miss | ✓ |
| Hit | Miss | 任意 | ✗ |
| 任意 | Miss | Hit | ✗ |
背后只有两条逻辑链。
第一条
TLB 中有有效页表项:
说明对应页面必然已经在主存。
因此:
第二条
页面根本不在主存:
Cache 作为主存内容的副本,自然也不可能保存这个页面对应的数据。
因此:
理解这两句话,就不用死背组合表。
六十一、Cache 和页表地址字段不要混
这一点综合题特别重要。
假设:
页大小 = 4KB
Cache块 = 64B那么页内偏移:
Cache 块内偏移:
二者不是同一个东西。
虚实地址转换保持的是:
12 bit Page Offset而 Cache 访问使用:
最低6 bit作为Cache Block Offset所以:
页 ≠ Cache 块。
通常一个页包含很多个 Cache 块。
例如:
即一个 4KB 页面对应 64 个 Cache 块。
六十二、页大小大还是小?
资料在本章开头就把这个问题列为思考题。
本质上是权衡。
页大:
一次从磁盘调入的数据更多,页表项更少,页表更小,也容易利用空间局部性;但可能把很多暂时没用的数据也调进来,并增大页内浪费。
页小:
内部浪费较小,分配更灵活;但页数量增加,页表更大,页表/TLB 管理压力提高。
所以不能说:
越大越好。
也不能说:
越小越好。
它是一个典型的工程折中。
六十三、段式虚拟存储器
分页按照:
固定大小。
分段则按照:
程序逻辑结构。
例如:
代码段
数据段
栈段
...虚拟地址:
段号 | 段内偏移段表项包含类似:
段基址
段长
有效位
...地址转换:
段号
↓
查段表
↓
得到段基址
↓
检查段内地址是否合法
↓
段基址 + 段内地址
↓
物理地址资料指出分段的优点是逻辑结构清晰,便于共享和保护;缺点是段长可变,容易产生外部碎片。
六十四、分页与分段的本质区别
| 页 | 段 |
|---|---|
| 固定大小 | 可变大小 |
| 物理划分思想 | 逻辑划分思想 |
| 对程序员透明 | 体现程序逻辑结构 |
| 容易内部碎片 | 容易外部碎片 |
| 共享保护语义弱一些 | 更适合逻辑共享保护 |
考试里不要只记:
页固定、段可变更深层本质是:
页为了方便物理内存管理;段为了匹配程序逻辑结构。
六十五、段页式
段页式就是把两者叠起来:
程序
↓
先按逻辑分段
↓
每个段内部再分页虚拟地址:
段号 | 段内页号 | 页内偏移地址转换:
段号
↓
段表
↓
找到该段的页表
↓
段内页号
↓
页表
↓
物理页号
↓
PPN | 页内偏移好处:
有段的逻辑共享、保护能力;
又有页式固定大小分配的便利。
缺点:
地址转换层次更多,开销更大。
六十六、Cache 和虚拟存储器到底有什么共同点?
这个问题非常适合把整个第 3 章连起来。
资料总结两者都依赖:
局部性、分块、地址映射、替换策略等。
但它们解决的问题不同:
| Cache | 虚拟存储器 |
|---|---|
| 解决 CPU—主存速度差 | 解决主存容量不足 |
| Cache ↔ 主存 | 主存 ↔ 磁盘 |
| 块较小 | 页通常较大 |
| 缺失代价相对低 | 缺页代价极高 |
| 主要硬件管理 | OS + 硬件协作 |
| 对程序员透明 | 对应用程序员透明 |
一句话:
Cache 让主存“看起来更快”;虚拟存储让主存“看起来更大”。
六十七、第 3 章综合题万能解题流程
以后遇到一道同时出现:
虚拟地址
页表
TLB
Cache
主存不要直接盯着二进制乱算。
固定顺序:
A. 先处理页面
页大小:
立即得到:
拆:
VPN | Page OffsetB. TLB
根据 TLB 是全相联还是组相联,拆 VPN。
查询:
Tag + ValidC. 页表
TLB Miss:
页表基址 + VPN×页表项大小找到 PTE。
看:
Valid
PPN
Dirty
...D. 形成物理地址
PPN | Page OffsetE. 再处理 Cache
块大小:
得到:
F. 算 Cache 组数
路组相联:
G. 算组号位数
H. 算 Tag
最后才:
查组 → Tag 比较 → 判断 Hit/Miss → 必要时替换。
这是我最建议你把它练成“肌肉记忆”的流程。
六十八、本章最容易错的十个地方
-
存取时间 ≠ 存取周期,通常存取周期更大。
-
SRAM 不刷新,DRAM 要刷新;DRAM 还是破坏性读出。
-
DRAM 的行列地址复用是复用物理引脚,不是把完整地址位数减少了。
-
DRAM 刷新是按整行刷新,不是每个存储单元单独刷新。
-
低位交叉:
它主要提高吞吐率,不是缩短单体存取周期。
-
位扩展增加数据位宽;字扩展增加地址数量。
-
Cache 总容量不能只算数据,还可能有 Tag + Valid + Dirty + 替换信息。
-
直接映射没有替换选择,所以不需要替换算法。
-
TLB Miss 不等于 Page Fault。
-
页内偏移和 Cache 块内偏移不是一个概念。
六十九、最后把整章压缩成一张逻辑图
存储系统
│
┌────────────────┴────────────────┐
│ │
为什么分层? 具体怎么实现?
│ │
速度/容量/成本矛盾 ┌─────────┴──────────┐
│ │ │
▼ ▼ ▼
Cache—主存—辅存 主存硬件 外存
│ │ │
│ SRAM / DRAM HDD / SSD
│ │ │
│ 地址复用/刷新/行缓冲 容量/存取时间
│
┌──────┴──────────────────────┐
│ │
Cache 虚拟存储
│ │
解决速度 解决容量
│ │
局部性 局部性
│ │
主存块 页面
│ │
映射 页表
│ │
直接/全相联/组相联 TLB
│ │
替换 缺页
FIFO/LRU │
│ │
写策略 段式 / 段页式
│
命中率/平均访存时间如果为了 408 考试得分来安排这一章的复习时间,建议优先把四大模块彻底练熟:1 Cache 地址划分与总容量;2 TLB + 页表 + Cache 综合地址转换;3 主存芯片字/位扩展与地址范围;4 低位交叉存储器带宽和体冲突。 其中前两个是这一章真正的核心,资料本身也明确将 Cache、虚拟存储器以及二者结合的地址翻译列为重点。
接下来再学 DRAM 地址复用、刷新和行缓冲,最后用磁盘、SSD、存储器分类这些相对独立的知识补全选择题即可。