存储系统
存储系统把程序产生的地址变成实际的数据访问。组原侧重 Cache、主存与访问时间,操作系统侧重地址空间、页表和页面置换;综合题经常把两边串起来。分清“地址翻译”和“数据访问”,才能知道每一次未命中究竟会引起什么操作。
这个板块包含哪些知识
存储系统图谱先讲层次,再展开一次访问。本页按这个关系组织:
- 存储层次与局部性:寄存器、Cache、主存、外存的速度和容量关系,时间局部性与空间局部性。
- Cache:映射与地址字段、有效位和标记、替换策略、写策略、命中率与缺失损失。
- 虚拟存储:虚页和页框、单级与多级页表、TLB、保护与缺页处理。
- 页面管理:驻留集、FIFO / LRU / CLOCK,以及访问序列引起的页面状态变化。
对一次典型访问,可以先沿 虚拟地址 → 地址翻译 → 物理地址 → Cache / 主存 追踪。TLB 缺失可能只需查页表,缺页才需要操作系统调页;Cache 缺失通常是从主存取块。三种事件的代价不能混算。
磁盘文件如何组织,另见文件系统与磁盘。
常见考点与题型
综合题可能组合下面几类小问。先看要求求什么,再确定需要调用哪些知识。
| 题型 | 题干中的线索 | 解题思路 |
|---|---|---|
| 地址字段与硬件容量 | 容量、路数、块大小、页大小 | 统一编址单位,先算块数或组数,再取位数;区分数据区与总容量 |
| 地址翻译与表项定位 | 虚拟地址、页表基址、表项大小 | 逐级提取索引并定位表项,取出页框号后拼接原偏移 |
| Cache / 页面访问序列 | 初态、替换策略、读写序列 | 按次记录映射、命中和状态更新,必要时记录脏块写回 |
| 访问次数与平均时间 | TLB、Cache、主存、缺页代价 | 先画题设串行或并行路径,再按各路径概率或实际次数累计 |
| 程序访问与局部性 | 数组、循环顺序、步长 | 把代码转换成地址序列,比较块内复用与组冲突,必要时逐次模拟 |
解题前要弄清的关系
地址类题可以先沿下面两条链定位问题;替换与性能题还需要记录访问序列、初态和各分支的代价。
一、地址翻译链:VA → PA 的实质是 VPN → PPN,页内偏移原样抄下来
VA = 虚页号 VPN ‖ 页内偏移—— 拆分点只由页大小决定,见 页面划分和地址结构VPN → TLB—— 一个装页表项的小 Cache,全相联或组相联;命中直接拿到 PPN,见 TLBVPN → 页表—— 只在 TLB 缺失时走;多级则先页目录再页表,见 多级页表PA = 页框号 PPN ‖ 页内偏移(偏移位不变,可用于判断哪些索引位不必等待地址翻译)
二、Cache 访问链:PA → 数据,靠三段地址定位
PA = 标记 Tag ‖ 组号 Index ‖ 块内地址 Offset—— 见 cache 地址结构- 组号选中唯一一组 —— 直接映射是「组内一行」,n 路组相联是「组内 n 行」,全相联只有一组,见 cache 和主存映射方式
- 组内逐路比 Tag 并查有效位 —— 命中按 Offset 取字;缺失访主存调入整块
- 缺失后两件事:替换算法(LRU / FIFO / 随机)与写策略(写直达或写回,搭配题设的写分配或非写分配),见 替换算法、cache 写策略
两条链串起来就是一次完整访存,草稿上默画一遍,做题时只需往上填数:
虚拟地址 ─┬─ 虚页号 ─→ TLB ─命中→ 页框号
│ └缺失→ 页表(多级先查页目录)─→ 页框号
│ └ 有效位 0 → 缺页异常 → 磁盘
└─ 页内偏移 ──────────────────┐
↓
物理地址 = 页框号 ‖ 页内偏移
├─ 标记 + 组号 ─→ Cache ─命中→ 送 CPU
└─ 块内地址 └缺失→ 访主存,调入整块
完整时序见 访存过程。
各类题的解法
地址计算先确定字段宽度,再代入地址;命中率、置换和时间题还要继续分析访问过程。
第一步:所有容量换算成位数
| 题干给的量 | 直接推出 | 关系 |
|---|---|---|
| 页大小 | 页内偏移位数 | log₂(页大小) |
| 主存块大小 | 块内地址位数 | log₂(块大小) |
| Cache 数据区容量、路数、块大小 | 组数、组号位数 | 组数 = 容量 ÷ (路数 × 块大小) |
| 物理地址位数 | 标记位数 | 物理地址位数 − 组号位数 − 块内地址位数 |
| 虚拟地址位数 | 虚页号位数 | 虚拟地址位数 − 页内偏移位数 |
| 页表基址、页表项大小 | 页表项地址 | 基址 + 表项号 × 表项大小 |
这张表填完,「占几位」「在哪一组」「页表项地址是多少」这几类小问就已经有答案了。会失分的情况通常是没填完就急着算下一问。
第二步:把链实例化
在草稿上重画那条链,把第一步算出的位数标在每一段上:虚页号几位、页内偏移几位、标记几位、组号几位。标出小问涉及的层次;组合题可能跨多个位置,先定位再计算,不要先套公式。
第三步:按问法对号入座
| 小问的问法 | 落在链上哪一层 | 固定动作 |
|---|---|---|
| 某虚拟地址的物理地址是多少 | 页表 | 页框号 × 页大小 + 页内偏移;位数对齐时直接拼接 |
| 页目录项 / 页表项的地址 | 页表 | 基址 + 表项号 × 表项大小,虚地址和物理地址各算一次 |
| 在 Cache 的哪一组 / 哪一行 | Cache | 取物理地址里的组号位段,不要用整个地址去除 |
| 命中率 / 缺失率 | Cache | 若从块边界顺序读完每块、每块仅首次缺失且无其他冲突,命中率为 (k−1)/k;一般情况按序列统计 |
| 平均访问时间 | Cache | 命中时间 + 缺失率 × 缺失损失 |
| 缺页次数 | 页表 | 无置换时按首次访问页数算;有置换时按完整访问序列模拟 |
| 按行还是按列遍历更快 | Cache | 先看步长和块内复用,再检查容量、映射冲突与初始状态 |
页表题常用两类计算:先用表项号定位页表项,再用页框号拼出物理地址。2013#46、2020#46 和 2024#45 是同一套题,其中 2024 年多绕一层——页表自己也放在某个页里,也要被翻译一次。
两种变形
以下“虚拟地址直接索引 Cache”讨论以题目明确采用 VIPT(Virtually Indexed, Physically Tagged)模型为前提;未说明时仍应按题设给出的访问顺序判断。
在上述 VIPT 模型中,让索引完全落在页内偏移中的条件是:组号位数 + 块内地址位数 ≤ 页内偏移位数。满足时这些位在虚拟地址和物理地址中完全相同,不必等地址转换结束就能开始索引 Cache。2011#44、2016#45 和 2019#46 靠它推理,2025#43 直接问「VA 中哪些位可以作为 Cache 索引」。
题干给了汇编或 C 代码,是把地址藏起来的写法,固定三步:
- 从代码里取数组首地址和元素大小,算出目标元素的虚拟地址;
- 把这个虚拟地址送进上面那条链;
- 遇到“哪种遍历顺序更好”,先比较步长与块内复用,再检查映射冲突。
命中与替换:每次访问后更新状态
可以列“地址 / 页号或块号 / 组号 / 命中否 / 淘汰对象 / 更新后状态”六列。LRU 要在命中时更新最近使用次序,FIFO 的入队次序则不会因为命中改变;写回 Cache 淘汰脏块时,还要计入写回流量或时间。
例如两页框、初始为空,访问 A,B,A,C,B:LRU 在访问 C 时淘汰 B,最后访问 B 又缺页,共 4 次;FIFO 在访问 C 时淘汰 A,最后 B 仍在,共 3 次。先明确策略,再计数,不能只数不同页面。
平均时间:先区分“额外损失”和“总耗时”
若命中耗时 2 ns,缺失后额外花 50 ns,缺失率 10%,则平均时间为 2+0.1×50=7 ns。若题目说缺失访问从开始到完成共 50 ns,则应算 0.9×2+0.1×50=6.8 ns。
TLB 与 Cache 组合题也先列各分支实际经过的部件,再加权。只有题设允许并行的步骤才能取重叠时间,串行步骤需要相加。
历年题索引
框架和流程是通用的,下面这张表用于比较地址翻译、数据访问和状态变化的组合方式,练完一年再回上面对一遍流程。
| 真题 | 主线 | 这一年特别问了什么 |
|---|---|---|
| 2009#46 | 请求分页 + TLB + LRU | 依次访问三个虚地址各需多少时间 |
| 2010#44 | Cache 直接映射 | 行遍历与列遍历的命中率谁更高 |
| 2010#46 | 分页 + FIFO / CLOCK | 同一个虚地址在两种置换算法下的物理地址 |
| 2011#44 | 页表 + TLB 四路组相联 + Cache | 用物理地址访问 Cache 时的字段划分 |
| 2012#43 | Cache 缺失率 + 主存带宽 | 由每秒缺失次数反推最低主存带宽 |
| 2012#45 | 驻留集 + 空闲页框链表 | 该置换策略是否适合时间局部性好的程序 |
| 2013#43 | Cache 块大小 + 突发传送 | 读一个主存块需几次突发传送 |
| 2013#46 | 二级页表 | 页目录号与页表索引的表达式 |
| 2014#45 | 指令 Cache + 缺页 + TLB | 哪条指令溢出、读磁盘与查 TLB 各几次 |
| 2015#46 | 二级页表 | 页目录加页表一共占多少页 |
| 2016#45 | TLB 全相联 + Cache 二路组相联 | 为什么 Cache 可以直写而页面用回写 |
| 2017#45 | 二级分页 + 机器指令 | 取第 1 条指令时访问页目录与页表的第几项 |
| 2018#44 | TLB + Cache 回写 | TLB 用 SRAM 还是 DRAM、有效位的作用 |
| 2018#45 | 二级页表 + 改进型 CLOCK | 由页目录号、页号、偏移反向合成虚拟地址 |
| 2019#46 | 分页 + 指令 Cache 四路组相联 | call 指令只可能在哪一组命中 |
| 2020#44 | Cache 八路组相联 + 直写 | 每行的 Tag 与 LRU 各几位、有没有修改位 |
| 2020#46 | 二级页表 + 二维数组 | 页目录项与页表项的物理地址 |
| 2021#44 | TLB 二路组相联 + LRU | 虚地址增到 32 位后表项要增加几位 |
| 2023#43 | 请求调页 + Cache 四路组相联 | 缺页次数与发生缺页的页故障地址 |
| 2024#44 | 页式管理 + 汇编 | a[i] 所在页号、数组至少占几页 |
| 2024#45 | 页表项定位 | 页表所在页的页号及其页表项 |
| 2025#43 | Cache 八路组相联 + 缺页 | 哪些 VA 位可作索引、平均访问时间 |
| 2025#46 | 进程虚拟地址空间分区 | PCB、ptr、字符串常量各在哪个区域 |
易错清单
- 组数忘了除路数。八路组相联的组数是「容量 ÷ 路数 ÷ 块大小」,少除一次路数,后面所有位数都错。
- 按字编址和按字节编址混用。这只决定主存单元宽度,不能由它推寄存器宽度。
- 不检查条件就用虚拟地址索引 Cache。见上面那一句不等式。
- 把“首次装入”当成所有缺页题的规则。无置换时缺页次数才等于首次访问页数;出现 FIFO、LRU、CLOCK 或明确的置换后,必须逐次模拟,缺页次数可能超过数据占用页数。
- 漏掉 TLB / Cache 初始为空造成的强制缺失。
- 二级页表只做了一次乘加。定位页表项和拼物理地址是两步,不能合并。