Appearance
Cache 写策略
2026 大纲 三(六)4 Cache 写策略。
读不出问题,问题全是写出来的
Cache 里放的是主存内容的副本。只要一直是读,副本和正本给出的值就永远相同,不会有任何麻烦。一旦开始写,"以谁为准"的问题就出现了——这一节从头到尾都在处理这一件事。
不一致有三个来源:CPU 写 Cache(Cache 行更新了而主存还是旧值)、I/O 设备直接读写主存(如 DMA,它绕过 Cache)、多个 CPU 共享主存(各有各的 Cache)。
🔴 写策略只解决第一个来源,后两个要靠额外的硬件协议(总线侦听、MESI 之类)。把"用了写直达就没有一致性问题了"当成结论,是这一节最大的误解。
写策略本身又是两个互相独立的维度,别把它们串成一件事:
- 写命中时要不要立刻同步主存 → 写直达 / 写回;
- 写不命中时要不要先把这块调入 Cache → 写分配 / 非写分配。
交互可视化
一、三个来源各自会出什么事
| 来源 | 说明 |
|---|---|
| CPU 写 Cache | Cache 行被更新,若主存未同步,则主存中的副本变成旧值 |
| I/O 设备直接读写主存 | 如磁盘通过 DMA 方式直接访问主存。若 Cache 中的内容已被 CPU 修改而主存未更新,传给 I/O 设备的就是旧值;反过来,若 I/O 设备修改了主存块,对应的 Cache 行就失效了 |
| 多个 CPU 共享主存 | 各 CPU 有各自的 Cache。某个 CPU 修改了自己 Cache 中的内容后,对应的主存块以及其他 CPU 中对应的 Cache 行都变为无效 |
后两条留给第六节的一致性协议,本节接下来的两个维度处理的都是第一条。
二、写命中:写直达与写回
写直达(Write Through):每次写 Cache 的同时也把这次写发往主存。缓解手段是写缓冲器(Write Buffer)——CPU 把写请求放进 FIFO 就继续执行,由缓冲器异步写入主存。
写回(Write Back):只写 Cache 并把脏位置 1,等这一行被替换时若脏位为 1 才整行写回主存。
| 写直达 | 写回 | |
|---|---|---|
| 主存同步时机 | 每次写都同步 | 该行被替换且脏时才同步 |
| 一致性 | 始终一致 | 可能不一致 |
| 脏位 | 不需要 | 每行 1 位 |
| 主存写带宽 | 高 | 低 |
| 对同一行的多次写 | 每次都产生一次主存写 | 只产生一次主存写 |
| 对 DMA / 多核 | 友好(外设直接读到新值) | 需一致性协议或显式冲刷 |
| 硬件复杂度 | 简单 | 较复杂 |
两种缓冲器服务的策略不同,别混:
| 配合哪种策略 | 缓存的是什么 | |
|---|---|---|
| 写缓冲器(Write Buffer) | 写直达 | 待写入主存的写请求 |
| 写回缓冲器(Write Back Buffer) | 写回 | 被替换出来的脏行 |
三条判据在这里定下来,本节的计算与判断题基本都靠它们:
🔴 写直达一定写主存、永远没有脏位;写回只动 Cache 并置脏位,主存写推迟到替换时。 这是最省事的两句速记。所以问"某次写操作是否访问主存"时,写直达的答案恒为是——哪怕 Cache 命中、哪怕 TLB 也命中,那一次写仍要落到主存。
🔴 脏位是 Cache 行的属性,每行 1 位,主存没有脏位——它是"被同步"的那一端。脏位的意义是避免无意义的写回:脏位为 0 说明这一行从未被写过、主存副本仍然有效,替换时直接覆盖即可。
⚠️ "写回时 Cache 与主存可能不一致"说的是存在不一致的可能,不是任何时刻都不一致。 某行若从未被写过(脏位 0),它与主存就是一致的。
另外两条常被拿来命题:写直达对 DMA 更友好——主存内容永远最新,DMA 直接读主存就拿到正确值;写回下最新数据可能还留在 Cache 里没写回,DMA 会读到旧值,必须靠一致性协议或显式冲刷。而写缓冲器只能吸收突发写——它让 CPU 把写请求丢进 FIFO 就继续执行,但缓冲器满了 CPU 照样阻塞,消除不了写直达的持续带宽压力。
三、写不命中:写分配与非写分配
- 写分配(Write Allocate):先把对应的主存块调入 Cache,再在 Cache 中执行写。预期这一块接下来还会被访问。
- 非写分配(No Write Allocate):直接写主存,不调入 Cache。预期这次写是一次性的,调入反而挤占 Cache 空间。
四、两种主流搭配与四象限
| 写命中 | 写不命中 | 为什么这样配 |
|---|---|---|
| 写直达 | 非写分配 | 反正每次写都要写主存,调入 Cache 并不能省下这次写,还白占一行 |
| 写回 | 写分配 | 写回靠"多次写合并成一次写回"获益,那就应该让数据先进 Cache,才有合并的机会 |
两种搭配各自内部自洽:同步得勤就别费劲调块,要靠推迟同步获益就得先把块拿进来。落到"动了哪几级存储"上:
| 情形 | Cache 内容 | 脏位 | 写主存 | 调入块 | 触发脏行写回 |
|---|---|---|---|---|---|
| 写直达 | 变 | 无此位 | 是 | 否 | 否 |
| 写直达 | 不变 | 无此位 | 是 | 否 | 否 |
| 写回 | 变 | 置 1 | 否 | 否 | 否 |
| 写回 | 变 | 置 1 | 否 | 是 | 被替换行脏则是 |
五、写策略对 Cache 容量的影响
Cache 每一行的非数据位随策略变化,这直接影响总容量的计算:
⚠️ 两个容量口径要分清:「标记阵列位数」只算非数据位(有效位
标记 脏位 替换位),「Cache 总容量」是它再加上数据区。两者相差约二十倍,题面问哪个就答哪个。
一道完整的容量演算:非数据位、标记阵列、总容量,以及改成写直达后差在哪一位(想核对自己会不会漏数脏位与 LRU 位时展开)
Cache 数据区容量 4 KB,4 路组相联,块大小 64 B,主存地址 32 位,写回策略与 LRU 替换。
第一步,定各字段位数。
- 块内地址
位 - 组号
位 - 标记
位
第二步,数每行的非数据位。 写回策略有脏位;4 路组相联的 LRU 需
第三步,算总容量。
第四步,改为写直达。 没有脏位,每行非数据位变为
六、多核环境下的写回
多核场景中,写回策略必须配合总线侦听协议(如 MESI)来维持一致性。MESI 用四个状态描述每个 Cache 行:
| 状态 | 含义 |
|---|---|
| M(Modified) | 本核独占,且已被修改、与主存不同步 |
| E(Exclusive) | 本核独占,但与主存同步 |
| S(Shared) | 多核共享,与主存同步 |
| I(Invalid) | 无效 |
大纲不要求展开 MESI,理解"写回策略在多核下需要硬件一致性协议支持"即可。
考点速记
- 一致性问题有三个来源(CPU 写 Cache、I/O 直接读写主存、多 CPU 共享主存),全部由写引出;写策略只解决第一个。
- 写命中与写不命中是两个独立维度:写直达每次同步、始终一致、永远没有脏位、带宽压力大;写回只写 Cache 并置脏位、替换时才按脏位决定要不要写回。主流搭配是写直达
非写分配与写回 写分配。 - 每行位数
有效位 标记 数据 [脏位] [LRU 位],后两项按写策略与映射方式决定;「标记阵列位数」不含数据区、「Cache 总容量」含,是两个不同的量。
这一节在真题里被考过的形式(下方「真题练习」逐题对应):
- 给一条读改写指令,问在直写策略下至少访问几次主存:"至少"意味着按最佳命中推算——取指、读操作数、查页表全都假定 TLB 与 Cache 命中,各 0 次;而直写下那一次写必然落到主存,所以答案就是 1。⚠️ 换成写回则连这 1 次也没有(只置脏位)。
- 算 Cache 行位数 / 标记阵列位数 / 总容量时的"有没有修改位"那一半:题面写"回写(Write Back)"就加 1 位脏位,写"直写(Write Through)"就不加。这一位乘上行数就是两种策略的容量差。
- 挑关于两个存储层次的错误叙述里涉及写策略的那一项:"Cache–主存层次可采用回写法,主存–外存层次通常采用回写法"是对的——两层都可以回写,错点通常不在这里而在映射方式那一项。
- 大题问答:为什么 Cache 可以采用直写,而修改页面内容时总是采用回写:同一条理由的两次应用——直写要求每次写都同步到下一级,对主存(几十到上百个时钟周期)尚可接受;对磁盘则是每改一个字节就启动一次毫秒级的机械操作,完全不可行。答题要落到下一级介质的访问代价相差三个数量级上。
易错:以为用了写直达就没有一致性问题。它只解决"CPU 写 Cache"这一个来源,DMA 与多核仍需协议。
易错:给直写策略的 Cache 行算上脏位。直写永远没有脏位。
易错:把"写回时不一致"理解成"任何时刻都不一致"。脏位为 0 的行与主存是一致的。
易错:把「标记阵列位数」与「Cache 总容量」混答。前者不含数据区。
易错:认为写缓冲器能消除写直达的带宽压力。它只吸收突发写,满了照样阻塞 CPU。
教材出处
- 袁春风《计算机组成与系统结构(第 3 版)》§7.5.5 cache 的一致性问题:一致性问题的三个来源(p247)
相关知识
Cache 的基本原理|Cache 和主存之间的映射方式|Cache 中主存块的替换算法|DMA直接存储器访问