据我所知,DSB,DMB和ISB是阻止指令重新排序的障碍。 我也可以为它们找到很多非常好的解释,但我很难想象我必须使用它们。
另外,从开源代码中,我不时会看到这些障碍,但很难理解它们的使用原因。举个例子,在Linux内核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。 你能举一些现实生活中的例子吗? 这将有助于更多地了解障碍。
答案 0 :(得分:32)
抱歉,不要像你要问的那样给你一个直截了当的例子,因为你已经在浏览Linux源代码了,你有很多可以解决的问题,而且它们似乎没有帮助。不要感到羞耻 - 每个理智的人至少最初都会被内存访问排序问题弄糊涂:)
如果您主要是一名应用程序开发人员,那么您就不必过多担心它 - 无论您使用什么并发框架都能为您解决它。
如果您主要是设备驱动程序开发人员,则可以非常直接地找到示例 - 只要先前访问中的代码存在依赖关系,就会产生影响(清除中断源,写入DMA描述符)执行访问(重新启用中断,启动DMA事务)。
如果您正在开发并发框架(或调试一个),您可能需要更多地阅读该主题 - 但您的问题表明表面的好奇心,而不是立即需要? 如果您正在开发自己的方法来在线程之间传递数据,而不是基于并发框架提供的原语,那就是所有意图和目的的并发框架。
Paul McKenney写了一篇关于内存障碍需求的优秀论文,以及它们在处理器中实际产生的影响:Memory Barriers: a Hardware View for Software Hackers
如果这有点太硬了,我写了一个由三部分组成的博客系列,它更轻巧一些,并以ARM特定视图结束。第一部分是Memory access ordering - an introduction。
但是,如果它是您所追求的示例列表,特别是对于ARM体系结构,您可能会比Barrier Litmus Tests and Cookbook做得更糟。
超轻型程序员的观点并非完全在架构上正确的版本是:
答案 1 :(得分:5)
在必须确保按特定顺序进行内存访问的情况下,通常需要使用内存屏障。出于多种原因可能需要这样做,通常在两个或多个进程/线程或硬件组件访问相同的内存结构时需要保持一致。
它经常在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;
因此,在将所有权转移到DMA硬件之前,始终设置len数据。否则,引擎可能会获得过时的数据,如指针或未更新的长度,因为CPU重新排序了内存访问。
在不同核心上运行的进程或线程也是如此。可以以类似的方式进行沟通。
答案 2 :(得分:3)
屏障要求的一个简单示例是自旋锁。如果使用比较和交换(或ARM上的LDREX / STREX)并且没有屏障来实现自旋锁,则允许处理器从内存中推测性地加载值并且懒惰地将计算值存储到内存中,并且这些都不需要发生按照指令流中的加载/存储顺序。
DMB特别阻止了DMB周围的内存访问重新排序。如果没有DMB,处理器可以在释放自旋锁后将存储重新排序到受自旋锁保护的内存。或者处理器可以在自旋锁被实际锁定之前或者被不同的上下文锁定时读取受自旋锁保护的内存。
unixsmurf已经指出了,但我也会指向Barrier Litmus Tests and Cookbook。它有一些非常好的例子说明你应该使用障碍的地点和原因。