嵌入式 C:内存屏障
更新时间:2026-09-21。嵌入式/裸机视角。volatile 的边界见 volatile 与硬件寄存器。
一句话回答
内存屏障用来约束 CPU/总线层面的访问顺序,弥补「volatile 只管编译器、管不住硬件重排」的缺口;ARM Cortex-M 有三条:DMB(数据访问彼此有序)、DSB(等之前的访存真正完成)、ISB(刷新流水线,让后续取指看到新配置),CMSIS 里对应 __DMB()/__DSB()/__ISB()。
一、volatile 与屏障的分工
volatile:阻止编译器缓存/删除/重排,但不保证 CPU 和总线怎么执行;- 内存屏障:约束硬件层面的完成与顺序。配 MMIO、改 MPU/向量表、多核通信用 volatile 之外,往往还要屏障。
二、三条屏障
| 指令 | 含义 | 典型用途 |
|---|---|---|
DMB | Data Memory Barrier,保证屏障前后的数据访问顺序 | 多核/多主控共享内存、锁 |
DSB | Data Synchronization Barrier,等之前所有访存完成才继续 | 写完 MMIO/缓存维护后等待生效 |
ISB | Instruction Synchronization Barrier,冲刷流水线 | 改 MPU、向量表、自修改代码后 |
CMSIS 调用:
c
__DSB(); // 等待前面的内存操作完成
__ISB(); // 让流水线重新取指,读到最新的 MPU/向量表设置三、典型场景
c
/* 修改 MPU 配置后 */
SCB->VTOR = (uint32_t)new_vector_table;
__DSB();
__ISB(); // 确保之后取指使用新向量表
/* DMA / cache 维护后等待操作完成 */
SCB_CleanDCache_by_Addr(addr, size);
__DSB(); // 等清/无效化真正完成再启动 DMA常见坑
- 用 volatile 代替屏障:编译器不重排 ≠ 硬件不重排,关键配置后仍可能失效。
- DMB 与 DSB 混用:只需要顺序时用 DSB 会白等、损失性能;需要「完成」时只用 DMB 又不够。
- 改完 MPU/向量表不加 ISB:流水线仍在执行旧指令,行为异常。
- 滥用屏障:每条 MMIO 都加 DSB 拖慢速度,应放在确有完成/顺序要求之处。
- 忽略缓存一致性:屏障不替代 cache clean/invalidate,DMA 场景两者都要。
总结
屏障管硬件层面的访存顺序与完成:DMB 保序、DSB 等待完成、ISB 刷新流水线;它补 volatile 之不能,常用于 MMIO 等待、MPU/向量表更新和多核/DMA 同步,但要用对类型、避免无谓性能损失。主机/多核视角另见 内存屏障与原子。