【嵌入式linux学习】Cache 策略:Write-Through / Write-Back + Allocation

Cache 策略:Write-Through / Write-Back + Allocation(分配策略)

  1. 写策略(Write Policy) :写内存时,Cache 和主存怎么同步:Write Through / Write Back
  2. 分配策略(Allocation Policy) :Cache miss(读缺失 / 写缺失)的时候,要不要把数据从主存加载进 Cache:Write Allocate / No-Write Allocate

写策略 = 写的时候怎么更新内存;

分配策略 = Miss 的时候要不要捞数据进 Cache

文章目录

  • [Cache 策略:Write-Through / Write-Back + Allocation(分配策略)](#Cache 策略:Write-Through / Write-Back + Allocation(分配策略))
    • [一、写策略(2 种)](#一、写策略(2 种))
      • [1. Write Through(WT,直写)](#1. Write Through(WT,直写))
      • [2. Write Back(WB,回写,ARM 默认常用策略)](#2. Write Back(WB,回写,ARM 默认常用策略))
    • [二、分配策略(针对 Cache Miss,有读缺失和写缺失)](#二、分配策略(针对 Cache Miss,有读缺失和写缺失))
      • [1. Write Allocate(WA,写分配)](#1. Write Allocate(WA,写分配))
      • [2. Read Allocate(No-Write Allocate,NWA,非写分配)](#2. Read Allocate(No-Write Allocate,NWA,非写分配))
    • 常用组合
    • [三、关键对比(结合 DMA)](#三、关键对比(结合 DMA))
    • 四、一些问题
      • [Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?](#Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?)
        • [① "最终都要落 DDR" 和 **"每次写都同步到 DDR" 是两回事**](#① “最终都要落 DDR” 和 “每次写都同步到 DDR” 是两回事)
        • [② 图像 buffer 不用 WT,会不会数据不一致?](#② 图像 buffer 不用 WT,会不会数据不一致?)
      • [问题 2:WB 模式下,每次调用 `dma_sync_for_cpu`,确实要消耗资源,为什么还要这么选?](#问题 2:WB 模式下,每次调用 dma_sync_for_cpu,确实要消耗资源,为什么还要这么选?)
        • [先明确 `dma_sync_single_for_cpu` 在 ARM WB 下做什么](#先明确 dma_sync_single_for_cpu 在 ARM WB 下做什么)
        • [对比两种方案的开销(OV8858 1632×1224 RAW10)](#对比两种方案的开销(OV8858 1632×1224 RAW10))
        • [什么时候 WB+sync 的开销会变成瓶颈?](#什么时候 WB+sync 的开销会变成瓶颈?)
        • 补充容易混淆的坑
    • 总结

一、写策略(2 种)

1. Write Through(WT,直写)

写 Cache 的同时,同步写回主内存

  • 流程:CPU 写数据 → 写入 Cache 行 并且同时写入 DDR 主存
  • Cache 行永远和 DDR 保持一致
  • 脏位 (dirty bit):没有脏位,不需要标记脏

✅ 优点:

Cache 和 DDR 永远一致;一旦 DMA 访问这块内存,不需要刷 Cache,天然一致性好

❌ 缺点:

  • 每次写都走 DDR,写带宽开销巨大,性能差(为什么呢,最后会讲)
  • 适合少量写、对一致性要求极高的场景,图像帧 buffer 一般不用 WT

2. Write Back(WB,回写,ARM 默认常用策略)

写只写到 Cache,不立刻写 DDR;只有 Cache 行被淘汰 (evict) 时,才一次性刷回 DDR

  • 流程:CPU 写 → 仅更新 Cache 行,DDR 暂时不变
  • 引入 dirty bit(脏位):标记这行 Cache 数据和 DDR 不一致
    • dirty=1:Cache 最新,DDR 旧
    • dirty=0:Cache 和 DDR 一致

✅ 优点:

  • 大量写操作命中 Cache 时,减少 DDR 访问,性能高(Linux 内核默认就是 WB)

❌ 缺点:

  • Cache 和 DDR 数据不同步

Linux 普通内存(CMA 分配的可 Cache 内存)是 WriteBack。 Sensor 硬件 DMA 直接读 DDR,Cache 里如果还有脏数据,硬件读到旧数据,图像花屏。 所以必须:dma_sync_for_device 把脏 cache 刷到 DDR。 硬件 DMA 写完帧到 DDR 后,CPU 要读图像,需要 dma_sync_for_cpu,invalidate cache,扔掉 CPU 旧缓存,从 DDR 拿新帧。


二、分配策略(针对 Cache Miss,有读缺失和写缺失)

Read Allocate(读分配):读 Cache miss 时,把对应内存行加载进 Cache 绝大多数 CPU 默认开启 Read Allocate,基本都打开,很少关。

下面两个是写缺失时的策略(写的时候,这个地址不在 Cache 里)

1. Write Allocate(WA,写分配)

写缺失:数据不在 Cache → 先把 DDR 的数据读到 Cache 行,然后再在 Cache 里做写修改

先 load,再 write。

  • 搭配 WB 最常见。适合:连续多次重复写同一个区域(循环改写一块 buffer)
  • 代价:写 miss 多了一次读 DDR 操作

2. Read Allocate(No-Write Allocate,NWA,非写分配)

写缺失:数据不在 Cache → 不加载到 Cache,直接写 DDR,Cache 完全不参与

不会把这行数据读到 Cache 里

  • 适合:一次性大块写,写完不再复用这块内存(比如一次性写日志)
  • ARM Linux 默认:WriteBack + ReadAllocate + No-WriteAllocate

常用组合

  1. WriteBack + ReadAllocate + No-WriteAllocate(Linux 普通内存默认,CMA 内存就是这组)
    • 读 miss:捞进 cache
    • 写 hit:只写 cache,标记 dirty,不写 DDR
    • 写 miss:直接写 DDR,不把行读到 cache
  2. WriteThrough + ReadAllocate 直写,写同时更新 DDR,无脏位。很少用于大 buffer。

补充:ARM 中 uncache 内存:完全不走 Cache,所有读写直接访问 DDR,没有 WT/WB 概念 dma_alloc_coherent 拿到的就是 uncache 内存,不存在 cache 同步,代价是 CPU 读写很慢。

三、关键对比(结合 DMA)

属性 WriteBack WriteThrough Uncache
脏位 ✅有 dirty bit ❌无 ❌无
写操作 先写 Cache,淘汰才写 DDR 写 Cache 同时写 DDR 直接访问 DDR
Cache-DDR 一致性 不一致,需要 dma_sync 基本一致 天然一致
CPU 读写性能 最高 较差 最差
适用场景 CMA 流式 DMA,图像 buffer 寄存器 / 少量共享内存 一致性 DMA (dma_alloc_coherent)

四、一些问题

Write Through:为什么每次写都走 DDR?数据不是本来最终就要落 DDR 吗?图像 buffer 不用 WT 难道不会不一致?

① "最终都要落 DDR" 和 "每次写都同步到 DDR" 是两回事

先理解 WT 的硬件行为:

Write Through:CPU 写 Cache 行,同时并行发起写 DDR 请求。Cache 只是副本,每一次 store 指令,都要往 DDR 发写事务。

WriteBack:CPU 写只改 Cache,不立刻发起 DDR 写;只有 Cache 行被淘汰 evict,或者主动 clean,才一次性批量写到 DDR。

举个例子:CPU 在图像 buffer 里连续写 100 个字节:

  • WT:100 次写操作 → 100 次 DDR 写事务。哪怕这 100 字节在同一个 Cache 行,每一次 CPU 写都触发 DDR 访问。
  • WB :100 次写命中同一 Cache 行 → 只修改 Cache,0 次 DDR 访问 ;等到这一行要被挤出去,一次性1 次 burst 写把整行(64B)刷到 DDR。

最终数据确实都会落到 DDR,但是访问 DDR 的时机、次数、带宽开销完全不一样 。 WT:写操作即时同步 ;WB:写操作延迟批量同步。

② 图像 buffer 不用 WT,会不会数据不一致?

不是硬件自动不一致,是 "没有手动同步才会不一致"。

  • WT:硬件保证写 Cache 同时写 DDR → Cache 和 DDR 永远一致。DMA 硬件读 DDR,直接拿到最新数据,不需要软件做 cache 同步。
  • WB:硬件不会自动同步。CPU 修改数据留在 Cache,DDR 是旧值。此时 DMA 直接读 DDR,读到旧数据 → 花屏。 👉 但 WB 提供了软件主动同步接口(dma_sync_xxx) ,在合适的时间点一次性同步,而不是每次 CPU 写都同步。

核心权衡: WT:硬件每次写自动同步,带宽开销持续存在 WB:平时不用同步,只在 CPU 和硬件交接 buffer 的那一刻,一次性同步一次

图像场景:一帧 2.4MB,CPU 可能只会在帧处理完成后交接一次 buffer,只做 1 次同步; 如果用 WT,CPU 每一次写像素都访问 DDR,持续占用 DDR 带宽,30fps 大分辨率场景带宽压力巨大。 这就是图像 buffer 不用 WT 的根本原因。

问题 2:WB 模式下,每次调用 dma_sync_for_cpu,确实要消耗资源,为什么还要这么选?

✅ 一句话结论:开销确实存在,但是相比全程 WT 的持续带宽开销,这个一次性开销小得多,收益远大于代价。

先明确 dma_sync_single_for_cpu 在 ARM WB 下做什么

Sensor(DMA 硬件)写完一帧到 DDR 之后,CPU 要读这帧图像: 调用 dma_sync_for_cpu → invalidate 对应 buffer 对应的 Cache 行 含义:告诉 CPU,Cache 里这一块区域的副本作废;CPU 后续访问这块内存,必须从 DDR 重新读新数据。

⚠️ 注意:这个场景不需要 clean!因为是硬件写 DDR,CPU 之前只是读,没有写这块 buffer,Cache 行不会有 dirty 标记。所以只是 invalidate,开销会比 clean 小。

对比两种方案的开销(OV8858 1632×1224 RAW10)
  1. 方案 A:WT 内存 CPU 读 / 写像素时,每一次内存访问都要访问 DDR。 只要 CPU 在处理图像(裁剪、统计、isp 后处理),全程持续占用 DDR 带宽 。30fps 持续跑,带宽压力一直在。 带宽开销:持续生效,每一次像素读写都产生 DDR 流量。
  2. 方案 B:WB + dma_sync_for_cpu(流式 DMA,CMA 内存)
    • Sensor 写帧阶段:CPU 不碰 buffer,没有任何 cache 开销
    • 硬件写完一帧,只执行一次 invalidate,把这一帧对应的 cache 行作废
    • 之后 CPU 处理这帧图像:全部走 Cache,读像素在 L1/L2 cache 命中,DDR 访问极少,处理速度快很多
    • 处理完,交给下一次 DMA 前,按需做 clean 同步(如果 CPU 修改了 buffer)

👉 开销模型区别: WT:细粒度、持续的硬件开销 (每一次 CPU 写都走 DDR) WB+sync:粗粒度、一次性的软件触发开销(一帧只同步 1 次)

只有在 buffer 交接点才付出同步代价;而图像处理的主体阶段,CPU 享受 Cache 高速读写。

什么时候 WB+sync 的开销会变成瓶颈?
  • 分辨率极大、fps 极高,每帧同步的 cache 操作耗时累积;
  • buffer 非常零散,同步要遍历大量不连续 cache 行;
  • CPU 核心负载已经拉满,cache 操作挤占 CPU 流水线。

在 OV8858 这种 1632x1224@30fps 场景:单帧 invalidate 开销很小,完全可接受。

补充容易混淆的坑
  1. dma_sync 不是每次都刷全部 2.4M 内核会记录 buffer 的有效范围,dma_sync 只会操作buffer 占用的 Cache 行,不是无脑遍历整个地址空间。
  2. 区分两种场景的同步操作(非常重要,摄像头必考)
  • CPU → DMA 硬件(CPU 填 buffer,交给 sensor DMA 读取):dma_sync_for_device → clean,把 Cache 里脏数据刷回 DDR,保证硬件读到最新数据
  • DMA 硬件 → CPU(sensor 写完帧,CPU 读图像):dma_sync_for_cpu → invalidate,丢弃 CPU 旧 Cache 副本,从 DDR 拿硬件新写的数据

只有 CPU 曾经写过这块 buffer,才会产生 dirty 行,才需要 clean;单纯硬件写完,CPU 只做读,只需要 invalidate。

总结

WT 是每次 CPU 写都同步 DDR,一致性由硬件保证,但每一次写都占用 DDR 带宽,持续消耗带宽,图像大数据场景性能差。 WB 不会每次写落 DDR,存在数据不一致风险,但我们只在 CPU 和 DMA 硬件交接帧 buffer 时,调用 dma_sync 做一次性 cache 操作。 虽然 dma_sync 有开销,但只是帧与帧之间少量一次性开销;图像处理阶段 CPU 可以利用 Cache 加速,整体带宽和性能远优于 WT。

相关推荐
峥无1 小时前
Linux NPTL线程:创建/终止/等待/分离/栈布局内核原理|线程下篇
linux·运维·线程
m0_587383002 小时前
24 小时自助健身房系统软件开发实战指南与案例解析
java·spring boot·spring·系统架构·需求分析
是jin奥2 小时前
Ubuntu 访问 Windows 共享目录
linux·运维·ubuntu
星恒随风3 小时前
Linux基础IO万字详解:从文件描述符fd到重定向、FILE缓冲机制及MiniShell升级
linux·运维·笔记·学习·状态模式
yunwei373 小时前
eBPF 入门开发实践教程一:Hello World,基本框架和开发流程
linux·后端·性能优化
小雪崩3 小时前
嵌入式学习 day65:Linux驱动进阶——pinctrl 子系统和 platform 驱动框架
linux·学习·驱动
shixiexunnie3 小时前
Raw NAND 驱动库学习
驱动开发·学习
wixzjsh3 小时前
嵌入式Linux系统启动与内核驱动开发全解
linux·运维·驱动开发
喜欢打篮球的普通人3 小时前
MiniMind 学习笔记(二十二):数学 Cookbook——把概率空间、期望、似然一次讲明白
人工智能·笔记·学习