- Cache / DMA / 内存,在芯片里各自“眼里看到的世界”到底是什么样的?
- 为什么它们天生就不一致?
- 为什么“什么都不做”在工程上几乎一定是错的?
一、先统一一个世界观:芯片里根本不存在“同一份数据”
这是理解所有一致性问题的钥匙。
很多工程师的直觉是:
内存里有一块地址,
大家不都是在用它吗?
这是软件视角,但硬件不是这么工作的。
在真实芯片里,至少存在三个并行但不自动同步的视角:
- CPU 视角—— 通过 Cache 看世界
- DMA / 外设视角—— 直接看内存
- 系统视角—— 总线 + 时序 + 仲裁
它们看到的,
是“同一个地址”,
但不一定是同一份数据。
二、Cache 的世界:CPU 被“保护”得太好了
2.1 为什么 CPU 一定要 Cache?
因为如果没有 Cache:
- CPU 每一条指令
- 都要等内存
那 CPU 的性能可以直接腰斩。
Cache 的本质目的只有一个:
让 CPU 尽量感觉不到内存的存在。
2.2 Cache 在“说谎”吗?
不是。
Cache 只是遵守了一个假设:
在“只有 CPU 自己”的世界里,它永远是对的。
问题在于:
- DMA
- 外设
- 其他核
并不生活在这个世界里。
2.3 一个非常关键、但常被忽略的事实
CPU 写变量,大多数时候只是写进了 Cache。
内存什么时候更新?
- Cache line 被替换
- 显式 Clean
- 系统空闲
这意味着:
“我刚写的值”,
可能只存在于 CPU 的私有世界里。
三、DMA 的世界:简单、直接、但“看不见 Cache”
3.1 DMA 的设计初衷
DMA 出现是为了做一件非常明确的事:
绕过 CPU,直接搬数据。
它的假设也很简单:
内存里是什么,我就搬什么。
3.2 DMA 的“盲区”
DMA:
- 不知道 Cache 是否存在
- 不知道 CPU 是否刚写过数据
- 不知道哪些数据还没落到内存
所以:
DMA 永远认为自己看到的是“真实世界”。
而这,正是冲突的起点。
四、为什么“一致性”不是默认就有的?
很多人会问:
硬件为什么不帮我自动处理好?
答案很现实:
4.1 自动一致性的代价极高
- 复杂的硬件协议
- 更高功耗
- 更低确定性
4.2 很多系统不允许不确定性
尤其是:
- 实时系统
- 安全系统
所以:
一致性,被设计成“你要,就自己负责”。
五、事故开始之前:一个最常见的“无辜代码”
buf[0]=0x55;start_dma(buf);你觉得发生了什么?
CPU 以为:
我写完了。
DMA 以为:
我读到了。
现实是:
内存里,什么都没变。
事故,在这一刻已经注定了。
六、事故 1(完整展开):DMA 读到的是“旧数据”
现场表现
- 外设收到旧包
- 重试后又好了
真实时间线
- CPU 写 Cache
- Cache 未写回
- DMA 直接读内存
为什么你 Debug 看不出来?
因为:
- 单步
- 打 log
- JTAG
都在无意中帮你 flush 了 Cache。
七、事故 2:DMA 写完了,但 CPU 看到的还是旧值
现场表现
- DMA 完成中断到了
- CPU 读 buffer 是旧的
真正原因
- DMA 写的是内存
- CPU 还在读 Cache
工程真相
DMA 完成 ≠ CPU 可见。
八、事故 3:只在 Release / O2 才出现
为什么这是“经典事故”?
因为:
- O2 改变访问顺序
- Cache 行为改变
- 时序窗口被放大
你不是引入了 Bug,
你只是让 Bug 终于有机会发生。
九、事故 4:多核系统的“平行宇宙”
现场
- Core0 写 flag
- Core1 永远等不到
根因
- 每个核都有自己的 Cache
- 没有硬件一致性
同一地址,
在不同核里,是不同世界。
十、事故 5:Cache 操作导致系统“更不稳定”
错误修复方式
clean_all_cache();后果
- 总线瞬间被占满
- 中断延迟暴涨
Cache 操作,
不是越多越安全。
十一、事故 6:Cache + DMA + 中断 → 死锁
真实链路
- CPU Clean Cache 关中断
- DMA 等中断
- CPU 等 DMA
这是系统级死锁,不是代码死循环。
十二、事故 7:安全数据“被改”,但你找不到凶手
真相
- DMA 没权限意识
- Cache 延迟暴露
安全事故,
常常最先表现为一致性事故。
十三、你真正应该记住的模型
一致性不是“做没做 Cache 操作”,
而是“三个世界有没有在关键时刻对齐”。
- CPU 世界
- DMA 世界
- 系统时序世界
十四、工程级生存原则(请背下来)
- DMA Buffer 永远有明确 Cache 策略
- 不知道怎么同步,就不要用 Cache
- Debug 稳定 ≠ 系统稳定
- 多核从来不是“自动一致”
十五、一句真正的收尾话
Cache / DMA / 一致性问题,
不是技术细节,
而是系统世界观。
一旦你真正理解了这个世界观,
这些事故,
你会在代码还没写完之前,就已经预见到。