CPU缓存的访问机制
2026/6/10 6:58:31 网站建设 项目流程

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. 命中路径
    • 选路信号控制一个多路选择器,挑中 Way[i] 的 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就是盒子上的便利贴。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询