CPU缓存的访问机制

1. 缓存里面到底存了什么?

32KB L1 Data Cache,8 路组相联,64 字节 Cache Line 为例。

可以把整个缓存看成一张 二维表

  • 共有 64 组(Set 0 ~ Set 63)
  • 每组有 8 路(Way 0 ~ Way 7)
  • 每路就是一个 缓存条目,里面包含:

|----------------|--------------------------------------------|
| 组成部分 | 说明 |
| Valid bit | 1 位,表示这个条目里是否存放了有效数据(上电或刷新后为 0,防止误匹配) |
| Dirty bit | 1 位(写回策略时用),表示这行数据被修改过,和内存不一致,将来替换时要写回 |
| Tag | 地址的高位部分,用来唯一标识这个 Cache Line 对应的是哪一块内存 |
| Data Block | 64 字节的连续数据 ,也就是常说的那个 Cache Line 数据 |
| (可能还有)LRU 位 | 用于记录访问历史,帮助替换策略选择丢掉哪一路 |

所以,"缓存行"不只是一个数据块,它是 Tag + 状态位 + 64 字节数据块 的整体。


2. 怎么判断数据在不在缓存里?地址被怎样拆分?

假设我们运行在 32 位物理地址 的 CPU 上(举例方便)。

缓存配置:32KB,8 路组相联,行大小 64 字节。

2.1 先算出三个部分的位数

  • 块内偏移 Offset :64 = 2⁶,需要 6 位,用于在 64 字节里选具体字节。
  • 组数 :总条目 = 32KB / 64B = 512。8 路组相联 ⇒ 组数 = 512 / 8 = 64 组。需要 6 位 组索引。
  • Tag 位 :剩下的高位 = 32 - 6 - 6 = 20 位

2.2 地址拆分图(32 位物理地址)

复制代码
|←--------- Tag 20 位 ---------→|← Index 6 位 →|← Offset 6 位 →|
  [31 .. 12]          [11 .. 6]       [5 .. 0]

|------------|----|-----------|--------------------------------|
| 字段 | 位数 | 位域(示例) | 作用 |
| Tag | 20 | 31:12 | 存放在缓存条目里,用来"对号"确认是哪一块内存 |
| Index | 6 | 11:6 | 选出 64 组中的哪一组 |
| Offset | 6 | 5:0 | 在 64 字节的块里,找到 CPU 真正要的那个字节 |


3. 查找过程:Tag、Index、Offset 如何联动

当 CPU 执行一条 LOAD 指令,比如 mov eax, [0x12345678],物理地址就是 0x12345678

  1. 拆地址
    • Offset = 低 6 位
    • Index = (地址 >> 6) 的低 6 位,比如得到 0x1A(第 26 组)
    • Tag = 剩下的高位 0x48D15 之类
  1. 选中组
    用 Index 找到第 26 组,这个组里有 8 个 Way。
  2. 并行比较 Tag
    把该组 8 路的 Tag 和 Valid 位 一起读出来(实际硬件同时做),和当前地址的 Tag 比对,而且要求 Valid=1。
  3. 命中 (Hit)
    假如 Way 3 的 Tag 完全相等,且 V=1,那就是命中。
    • 选中 Way 3 的 64 字节 Data Block
    • 用 Offset 从这 64 字节中提取所需的 4 字节(或 1 字节),返回给 CPU 寄存器
    • 更新 LRU 信息,表示 Way 3 刚被访问过
  1. 缺失 (Miss)
    如果 8 路里没有任何一个 Tag 匹配,或者 V=0,就是未命中。
    • 硬件暂停流水线,向下一级存储(L2 / L3 / 内存)发出"读取整块 64 字节"的请求
    • 数据回来后,放入当前组(第 26 组)的某一 Way,比如根据 LRU 选一个最近最少使用的 Way
    • 如果要替换的 Way 的 Dirty bit 为 1,说明它曾修改过,需要先把这 64 字节写回内存
    • 将新块的 Tag 写入该 Way,设置 Valid=1,Dirty=0
    • 然后像命中一样,从新块里用 Offset 取数据返回 CPU

4. 其他关键标志位:Valid & Dirty 等

  • Valid (V):开机时所有 Valid=0。不加这一位,垃圾 Tag 可能和数据匹配,读到错数据。
  • Dirty (D):只在写回(write-back)策略下有效。如果 CPU 写过这个缓存行,D=1,表示它比内存新。将来被替换时,必须写回内存;如果 D=0,可以直接丢弃。
  • LRU 位 / 伪 LRU 位:记录这一组里各 Way 的访问新旧顺序,用来决定踢谁。
  • MESI 等一致性状态位:多核系统里,标记该行是 Modified / Exclusive / Shared / Invalid,保证多核看到的数据一致。

5. 完整流程:从一条 LOAD 指令到拿到数据

物理地址访问 L1 Data Cache 为例,屏蔽 TLB 细节。

  1. 指令发出
    LOAD R1, [A](A 是物理地址)
  2. 地址拆分
    硬件抽取 Index(6 位)、Offset(6 位)、Tag(高位)
  3. 访问 L1 数据缓存
    • 用 Index 找到 Set
    • 读出该 Set 所有 8 路的 Tag、Valid、Dirty、Data 块
  1. Tag 比较 + Valid 检查
    • 比较器组并行工作:(Way[i].Tag == A.Tag) && Way[i].Valid
  1. 命中路径
    • 选路信号控制一个多路选择器,挑中 Wayi 的 64 字节数据
    • 再根据 Offset 做字节移位/选择,得到最终数据
    • 数据返回 CPU,LOAD 完成
  1. 缺失路径
    • 生成缺失请求(物理地址 A),发往 L2
    • L2 同样用它的 Index/Tag 查找,若 L2 命中则返回整块 64B;否则继续向 L3/内存
    • 最终 64 字节数据抵达 L1,填入指定 Set 的替换 Way
    • 更新 Tag、Valid、重置 Dirty,可能更新 LRU
    • 然后重新执行第 3 步(或直接旁路将数据同时返回 CPU)

6. 一个刻在脑子里的生活比喻:图书馆档案室

想象一个 图书馆档案室

  • 馆内有 64 个书架 → 这对应 64 组 (Set)
  • 每个书架有 8 层 → 这对应 8 路 (Way)
  • 每层可以放 一个档案盒 → 这就是一个 Cache Line 条目
  • 档案盒里正好有 64 页纸 → 这对应 64 字节数据块
  • 档案盒书脊上贴着 标签 (Tag) → 写明了"这是哪一本档案的第几卷"
  • 盒子上还有一张 "有效"便利贴 (Valid) → 空盒子没有便利贴,不能拿给读者
  • 如果有人在盒子里涂改了,就贴一张 "已修改"(Dirty) 便利贴

现在,有读者要查阅 地址 A 的第 K 页:

  1. 根据地址里的 书架编号 (Index),直接走到那个书架前。
  2. 扫一眼这个书架的 8 层,比较书脊上的标签(Tag)和地址里的 Tag,并且必须看到"有效"便利贴。
  3. 如果找到标签匹配、且有效的档案盒 → 命中
    从盒子里翻到 第 K 页 (Offset),交给读者。
  4. 如果这个书架 8 层都没有匹配的标签(或者盒子无效)→ 缺失
    管理员去仓库(内存)找到对应的档案盒,拿回来:
    • 如果书架满了,根据"最少借阅"记录选一层,把旧盒子取下来。
    • 如果旧盒子贴着"已修改",必须先把它的内容抄回仓库(写回)。
    • 新盒子放上去,贴上正确的标签和"有效"便利贴,把第 K 页交给读者。

这样一来:

  • 缓存行 就是那个连盒带签的档案盒。
  • 组相联 就是书架固定、但书可以灵活放在任意一层。
  • Tag/Index/Offset 就是找书架、看标签、翻页数的过程。
  • Valid/Dirty 就是盒子上的便利贴。

有了这个书架模型,整个缓存查找和替换的直觉就非常牢固了,以后很难再混淆。

相关推荐
OpsEye15 小时前
系统负载高一定是CPU问题吗?
运维·cpu·it
茶马古道的搬运工1 天前
Linux-Ubantu-贴士-建立Docker 沙盒(三)
操作系统
茶马古道的搬运工1 天前
Linux-Ubantu-贴士-apt的地盘
操作系统
带娃的IT创业者2 天前
穿越回 1980:解读微软开源的“最早 DOS 源码”与操作系统的原点
microsoft·微软·开源·操作系统·dos·源码解析·计算机历史
Seven972 天前
select、poll、epoll 到底有什么区别?一文讲透 I/O 多路复用
操作系统
磊 子3 天前
硬中断 软中断
后端·操作系统
mifengxing4 天前
操作系统(五)
linux·运维·服务器·操作系统·王道考研
apcipot_rain4 天前
计科八股20260605——软件生命周期、文档、死锁、地址转换、I/O控制方式、堆、无向图、连通图、最小支配集、逆关系、永真式
数据结构·操作系统·软件工程·计算机组成原理·离散数学
sulikey5 天前
个人Linux操作系统学习笔记7 - 进程理解
linux·笔记·学习·操作系统·进程·pid