术语: DMB – Data Memory Barrier DSB – Data Synchronization Barrier ISB – Instruction Synchronization Barrier
思考:内存屏障到底屏的什么?为什么要用内存屏障?乱序执行是怎样的一个乱序?为什么会出现乱序?
1、背景
DMB、DSB、ISB 看似三条简单的指令,网上有着一大堆博客、也有着一大堆的Linux Kernel教程,都会介绍这几个命令,简而言之,分别就是:数据内存屏障、数据同步屏障、指令同步屏障..... 虽然看似很简单,但是真的很难理解,因为其中蕴含着丰富的硬件基础原理,今年我们就剖开事务看本质,从最底层到应用实列,来理解这几个命令。
2、ARM是怎样执行指令的三级流水
如下图所示,ARM有三级流水线,分别是: 取指、译码、发射(issue)&执行在 取指、译码
阶段,是有序的。而是发射(issue)阶段,是无序的。
以上只是当前主流的架构 ,但并不意味着所有的Core都是这样的。如下贴出了一个表格,注意其中的 Outof order
、 Inorder
, 这里所说的也正式issue(发射)阶段的有序或乱序
这个时候,可能就会有同学发问了:对于 Inorder
的core,是不是就不需要DSB/DMB/ISB这样的指令了呢?当然不是,因为此时还要去看,你是不是multi-pipeline?还是single-pipeline 例如:A53、A7等,它们虽然是 Inorder
,但也是 dual-pipeline
(1) A53:In-order pipelinewithsymmetric dual-issue of most instructions
(2) A7的pipeline如下所示,具有5个端口(Integer、Multiply、Floating、Dual Issue、Load/Store),A7具有可同时提供到这些端口的两条指令的组合,即可以发出两条指令的有序流水线
所以,尽管你是 Inorder
的,但是你可以同时发出两条指令的流水线,后发射的也是有可能先执行完成的,所以整体来看还是无序的。即DMB/DSB/ISB还是需要的。
其实,我发现一个很有趣的现象:
- 只要in-order的core,都是dual-pipeline
- 只要是outer-of-order的core,都是single pipeline
in-order:顺序流水线, 是指处理器按照它们在内存中出现的顺序发出指令(issue)。下一条指令不会早于上一条指令发出,但是如果是多pipeline的,那么一次还是可以发送多条指令的out-of-order: 乱序流水线, 是指处理器可以不按照指令在内存中的顺序发出(issue)指令
in-order 和 out-of-order的区别如下所示:
3、DMB/DSB/ISB 指令的介绍
由于很多处理器的 Out-of-order execution(乱序执行)和 speculative execution(推测执行), 所以也意味着你的访问内存可能没有按照程序顺序进行 。在大多数的情况下,我们都无需关心访问的顺序,但有些情况下却是不得不考虑,例如:
- 在不同的threads/cores之间共享数据
- 传递数据给peripherals, 如DMA操作
- 修改指令所在的内存,如load一个程序到RAM
- 修改内存管理方案,如context switching
ARM Architecture提供了三个同步指令:
- DMB – Data Memory Barrier 数据内存屏障 确保位于 “DMB 指令前的所有显式内存访问” 早于 " DMB 指令后的显示内存访问"( 注意:data cache的操作也被看做是显示的访问)
- DSB – Data Synchronization Barrier 数据同步屏障 确保 (1)位于此指令前的所有显式内存访问均完成。(2)位于此指令前的所有缓存、跳转预测和 TLB 维护操作全部完( 注意:**cache/TLB/branch的维护操作是广播,那么要等待广播的完成)
- ISB – Instruction Synchronization Barrier 指令同步屏障 确保提取时间晚于 ISB 指令的指令能够检测到 ISB 指令执行前就已经执行的上下文更改操作的执行效果
很明显数据存储屏障(DMB)比数据同步屏障(DSB)限制少。下面也举了一些代码示例,注意看注释.
4、不同的观察者(observers)
core的instruction interface、 data interface、MMU table walker 被看做不同的观察者(observers)
如下一段代码中,有两个观察者, 由于乱序读写内存,可能会导致 I-cache
操作的是旧的数据
5、Barrier Qualifiers(限定符)
(1)、Shareability domain:
- Full System
- Outer Shareable
- Inner Shareable
- Non‐shareable
(2)、Accesses for which the barrier operates (before – after):
- Load – Load/Store (new to Armv8‐A)
- Store – Store
- Any – Any
6、示例
6.1、Mail box example
6.2 Speculation across barriers
6.3 Memory mapped peripherals
6.4 ISB示例
Context‐changing 操作:
- Cache、TLB、分支预测维护操作
- 系统寄存器的改变(如 SCTLREL1,TCREL1、TTBRn\_EL1...)
Context‐changing只有在下面事件之后才会被确认:
- taking一个异常
- 退出一个一次
- ISB
Instruction Synchronization Barrier (ISB)
- 确保ISB之后的指令 可以看到 ISB之前的Context‐changing
- 确保ISB之后的Context‐changing,仅影响ISB之后的指
7. One‐Way Barriers
- Load‐Acquire (LDAR) (1) 所有在LDAR之后的内存访问,必需在LDAR之后被观察到 (2) 所有在LDAR之前的内存访问,不受影响
- Store‐Release (STLR) (1)所有在STLR之前的内存访问 ,必需在STLR之前被观察到 (2) 所有在STLR之后的内存访问,不受影响
思考: 在Linux Kernel中你是怎样读写寄存器的?调用的哪个接口?答案:正是使用的ldar指令:
但是在一些代码中,如ATF等,也许是直接赋值的,这种方式显然没有ldar更严谨
8、Linux Kernel Barries API
![]
使用方式,例如:
作者:baron
文章来源:ARM精选
推荐阅读
- 思考: 什么时候需要disable MMU/i-cache/d-cache?
- 硬核:context switch的切换过程(TTBR0的切换/ASID的介绍)
- 思考:Linux Kernel的中断处理函数中是否会被其它程序(中断/异常)打断?
欢迎关注ARM精选专栏, 欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。