内存屏障是一个通用术语,用于指代一条或多条指令,它们强制处理器在执行加载(load)或存储(store)指令时进行同步事件。ARMv7-M 和 ARMv6-M架构都提供了三个内存屏障指令来支持内存顺序模型。这三个内存屏障指令分别是:DMBDSBISB

1 DMB、DSB和ISB之概述

(1)Data Memory Barrier(DMB):数据内存屏障
主要用于多核处理器系统中,不同的处理器可能同时执行数据内存传输指令。DMB指令确保在DMB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的数据内存传输指令都将在DMB执行之后开始执行,否则有些数据传输指令可能会提前执行。
(2)Data Synchronization Barrier(DSB):数据同步屏障
在计算机的体系结构中,处理器在执行指令时通常会利用指令流水线来提高性能。但也会产生一些问题,比如在多线程编程中,两个线程同时对共享的内存进行读写操作,由于读/写操作的重排序,就会导致数据的不一致。

当执行DSB指令时,它确保在DSB之前的所有显式数据内存传输指令都已经在内存中读取或写入完成,同时确保任何后续的指令都将在DSB执行之后开始执行。

(3)Instruction Synchronization Barrier(ISB):指令同步屏障
指令的流水线允许处理器同时执行多条指令的不同阶段,然而这样并行执行可能会导致一些问题,特别是涉及到上下文切换的情况,如实时操作系统的任务切换。当上下文切换时,可能指令流水线中的指令还在执行,而此时上下文已经改变,导致指令执行的结果不正确。

通过插入ISB指令,处理器会将流水线中的指令全部刷新,从而确保之前的指令不会影响后续指令的执行,并且后续指令将从正确的上下文开始重新获取。

tips:大多数CPU的体系架构在异常的入口和出口都有ISB的语义(自动执行)
当处理器执行代码时,可能会遇到异常情况,例如中断、系统调用、或者其他外部事件的触发。在这些情况下,处理器需要暂时中止当前的任务,转而处理异常事件,然后再返回到之前的任务继续执行。

具体来说,在进行异常进入之前,处理器会执行ISB操作。这样做的目的是刷新指令流水线,确保异常处理程序的指令是从正确的地址开始执行,避免异常之前的指令对异常处理程序造成干扰。

而在异常处理程序执行完毕后,处理器需要返回到之前被中断的任务继续执行。在进行异常返回之前,处理器同样会执行ISB操作。这样做的目的是刷新指令流水线,确保返回时从正确的地址重新获取指令,避免异常处理程序的指令对正常任务造成干扰。

(4)API
在C语言中,可以使用CMSIS或各种C编译器提供的函数来调用内存屏障指令。

内存屏障CMSIS函数MDK-ARM,DS-5和RVDS中集成的C编译器
DMB_DMB()_dmb(0xF)
DSB_DSB()_dsb(0xF)
ISB_ISB()_isb(0xF)
  • 这里的0xF表示指定一个完整系统屏障(Full System Barrier)操作,它将确保读写的屏障操作,适用于芯片内部的内存(如SRAM)和外部的内存(如自己接的HyperRAM),无论该内存是Shareable还是Non-Shareable。

2 三种指令的典型案例

1、DMB
DMB指令保证了两个内存访问能按正确的顺序执行。实际上DMB在Cortex-M的处理器中用得并不多,因为Cortex-M处理器不会重新排序内存事务(Memory transaction)。但如果想要软件能在其他ARM处理器上重用(如Cortex-M移植到Cortex-A),尤其是在多主系统中,DMB是必要的。下面举几个例子:
(1)DMA
在使用DMA控制器时,需要在CPU内存访问和DMA操作之间插入DMB屏障,以确保CPU当前的内存读写操作在DMA开始之前完成。

(2)多核系统中的信号量
在多核系统中,使用信号量进行核间同步。需要使用DMB来强制指定内存执行顺序,以避免潜在的竞态条件或数据不一致性。

当一个核要访问共享资源之前,它会先检查信号量的状态。如果信号量已经被另一个核获取,当前核就必须等待,直到信号量状态变为可用。这个等待过程需要保证在一个核释放信号量之后,其他核能够立即看到信号量状态的变化,而不是因为处理器优化或缓存导致的无效读取而产生错误。

在这里,DMB的作用就是强制执行内存顺序。通过在核获取信号量之前插入DMB屏障,确保在DMB之前的所有内存操作都完成。这样,在一个核释放信号量之后,其他核获取信号量的操作能够看到最新的信号量状态,从而实现正确的同步。

(3)多核系统中的邮箱
类似地,在核之间通过邮箱机制通信时,需要使用DMB来对邮箱的内存访问正确顺序,避免通信问题。

2、DSB
在Cortex-M处理器中,DSB可以用来:
(1) 确保对SCS(System Control Space)的修改在下一条指令执行之前生效

在ARM Cortex-M处理器中,SCS是一个特殊的内存区域,包含了一些系统控制寄存器和配置信息,用于管理处理器和系统的各种功能和特性。访问SCS的寄存器可以影响处理器的行为,例如启用或禁用特定的中断、配置时钟、设置系统控制位等。为了确保对SCS的修改在下一条指令执行之前生效,需要使用DSB指令进行数据同步。

(2)确保在执行特权级指令之前,内存中的数据已经更新
在ARM Cortex-M处理器中,一些特殊的指令如SVC(Supervisor Call,特权级调用)、WFI(Wait For Interrupt,等待中断)、WFE(Wait For Event,等待事件)等操作,涉及到特权级的转换或者等待系统事件发生,需要使用DSB指令。

3、ISB
ISB指令用于清空流水线,确保在ISB指令之前的所有上下文修改操作的效果被后续操作正确识别。有一个很典型的例子:
CONTROL寄存器的修改
在修改CONTROL寄存器后,应该使用ISB指令。比如我们修改CONTROL寄存器中的相关字段以进入特权模式,然后下一行代码就是一些特权操作,在这之前就需要使用ISB指令来让处理器正确识别新的特权级。

3 总结

本文详细说明了DMB、DSB和ISB三个指令的含义和使用时机。但大多数简单处理器不会对内存传输进行重新排序,因此,体系结构的需求和处理器的实现需求是不同的。例如,大多数应用程序可以在现有的Cortex-M处理器上正确运行,而无需使用任何内存屏障指令。

但是,如果要将应用程序移植到高端处理器,则内存屏障指令的遗漏可能会导致应用程序出现故障。如果要将软件移植到具有多个处理器的系统上,内存屏障的使用也很重要。例如,在多处理器系统中处理信号量时,应该使用内存屏障指令来确保系统中的其他处理器能够以正确的顺序观察到数据的变化。

ARM建议软件开发人员基于架构需求开发软件,而不是基于处理器特定的行为。这确保了软件代码的可移植性和可重用性。处理器特定的行为在相同架构的不同的发布版本之间也可能有所不同。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐