ARM 中屏障(DSB、DMB、ISB)的实际用例
Posted
技术标签:
【中文标题】ARM 中屏障(DSB、DMB、ISB)的实际用例【英文标题】:Real-life use cases of barriers (DSB, DMB, ISB) in ARM 【发布时间】:2013-03-07 16:05:38 【问题描述】:我了解 DSB、DMB 和 ISB 是防止指令重新排序的障碍。 我也可以找到很多很好的解释,但很难想象我必须使用它们的情况。
另外,从开源代码中,我不时看到这些障碍,但很难理解为什么要使用它们。举个例子,Linux kernel 3.7 tcp_rcv_synsent_state_process函数中,有如下一行:
if (unlikely(po->origdev))
sll->sll_ifindex = orig_dev->ifindex;
else
sll->sll_ifindex = dev->ifindex;
smp_mb();
if (po->tp_version <= TPACKET_V2)
__packet_set_status(po, h.raw, status);
其中 smp_mb() 基本上是 DMB。 你能给我一些你在现实生活中的例子吗? 这将有助于更多地了解障碍。
【问题讨论】:
这个问题是最近的一个例子:***.com/q/15003405/1163019 Cortex 程序员指南也有关于障碍 (11.2) 的部分。 infocenter.arm.com/help/topic/com.arm.doc.den0013c/index.html en.wikipedia.org/wiki/Memory_barrier 有可能有帮助的信息。 【参考方案1】:通常,在您必须确保内存访问以特定顺序发生的情况下,您需要使用内存屏障。这可能出于多种原因需要,通常当两个或多个进程/线程或硬件组件访问相同的内存结构时需要它,该结构必须保持一致。
它经常用于 DMA 传输。一个简单的 DMA 控制结构可能如下所示:
struct dma_control
u32 owner;
void * data;
u32 len;
;
所有者通常会设置为类似 OWNER_CPU 或 OWNER_HARDWARE,以指示允许两个参与者中的谁使用该结构。
改变它的代码通常是这样的
dma->data = data;
dma->len = length;
smp_mb();
dma->owner = OWNER_HARDWARE;
因此,数据和 len 总是在所有权转移到 DMA 硬件之前设置。否则引擎可能会得到陈旧的数据,例如未更新的指针或长度,因为 CPU 重新排序了内存访问。
在不同内核上运行的进程或线程也是如此。可以以类似的方式进行通信。
【讨论】:
【参考方案2】:抱歉,不会像您要求的那样给您一个直截了当的示例,因为您已经在查看 Linux 源代码,您有很多要查看的内容,但它们似乎没有帮助.没有什么可耻的——每个理智的人至少最初都会被内存访问顺序问题弄糊涂:)
如果您主要是应用程序开发人员,那么您很有可能无需过多担心 - 您使用的任何并发框架都会为您解决问题。
如果您主要是设备驱动程序开发人员,那么示例很容易找到 - 只要您的代码中存在对先前访问的依赖关系,该访问在其他访问之前已经产生了影响(清除了中断源,写入了 DMA 描述符)执行访问(重新启用中断,启动 DMA 事务)。
如果您正在开发一个并发框架(或调试一个),您可能需要更多地阅读该主题 - 但您的问题表明了一种肤浅的好奇心,而不是迫切的需要? 如果您正在开发自己的方法来在线程之间传递数据,而不是基于并发框架提供的原语,那么这就是并发框架的所有意图和目的。
Paul McKenney 写了一篇关于内存屏障的必要性以及它们对处理器的实际影响的优秀论文:Memory Barriers: a Hardware View for Software Hackers
如果这有点太硬核,我写了一个由 3 部分组成的博客系列,它更轻量级,并以特定于 ARM 的视图结束。第一部分是Memory access ordering - an introduction。
但是,如果它是您所追求的具体示例列表,尤其是对于 ARM 架构,您可能会比 Barrier Litmus Tests and Cookbook 做得更糟。
超轻量级程序员的观点和架构上不完全正确的版本是:
DMB - 每当内存访问需要对另一个内存访问进行排序时。 DSB - 每当需要在程序执行之前完成内存访问时。 ISB - 每当指令提取需要在程序中的某个点之后显式发生时,例如在内存映射更新之后或在编写要执行的代码之后。 (实际上,这意味着“此时丢弃所有预取指令”。)【讨论】:
很好的解释。不过,我不禁想知道这 3 条指令。基本上,你写的是: DMB:确保上面的代码完成。 DSB:确保上述代码完成。 ISB:确保上述代码完成。 (我应该阅读您提供的链接。)不过,“易失性”不会告诉编译器保证指令顺序吗? (最终通过插入上述命令之一等) @Illishar: volatile 会影响编译器生成指令的顺序,是的。这些指令会影响这些指令的结果何时保证在无序和/或多 CPU 系统中在架构上是一致的。 Paul McKenney 的论文是列出的资源中最完整的一篇,值得花一些时间阅读。至于说明,那不是描述所说的。进入并发世界时,要习惯于大量关注语义。【参考方案3】:屏障要求的一个简单示例是自旋锁。如果您使用比较和交换(或 ARM 上的 LDREX/STREX)实现自旋锁并且没有障碍,则允许处理器推测性地从内存中加载值并将计算值延迟存储到内存中,并且这些都不需要发生按照指令流中加载/存储的顺序。
DMB 特别防止内存访问围绕 DMB 重新排序。如果没有 DMB,处理器可以在自旋锁释放后重新排序存储到受自旋锁保护的内存。或者,处理器可以在自旋锁实际锁定之前或在它被不同的上下文锁定时读取受自旋锁保护的内存。
unixsmurf 已经指出了这一点,但我也会将您指向Barrier Litmus Tests and Cookbook。它有一些很好的例子来说明应该在哪里以及为什么应该使用障碍。
【讨论】:
以上是关于ARM 中屏障(DSB、DMB、ISB)的实际用例的主要内容,如果未能解决你的问题,请参考以下文章
ARM有几条memory barrier 的指令?分别有什么区别?
AQS同步组件-CyclicBarrier(循环屏障)解析和用例