Memory management 确定使用屏障(围栏)的位置

Memory management 确定使用屏障(围栏)的位置,memory-management,concurrency,linux-kernel,kernel,memory-barriers,Memory Management,Concurrency,Linux Kernel,Kernel,Memory Barriers,x86指令lfence/sfence/mfence用于在Linux内核中实现rmb()/wmb()/mb()机制。很容易理解,这些用于序列化内存访问。然而,在编写代码时确定何时何地使用这些代码要困难得多——在运行时行为中遇到错误之前 我很想知道,在编写/审查代码时,是否有可以检查的已知警告,这可以帮助我们确定必须在何处插入屏障。我知道这太复杂了,但是有没有经验法则或检查表可以帮助我们确定需要这些代码的地方呢?我的经验(不是在Linux内核中)是,有两种模式涵盖了绝大多数的防护需求 模式“Send

x86指令lfence/sfence/mfence用于在Linux内核中实现rmb()/wmb()/mb()机制。很容易理解,这些用于序列化内存访问。然而,在编写代码时确定何时何地使用这些代码要困难得多——在运行时行为中遇到错误之前

我很想知道,在编写/审查代码时,是否有可以检查的已知警告,这可以帮助我们确定必须在何处插入屏障。我知道这太复杂了,但是有没有经验法则或检查表可以帮助我们确定需要这些代码的地方呢?

我的经验(不是在Linux内核中)是,有两种模式涵盖了绝大多数的防护需求

模式“Send/receive”:线程1将数据发送到线程2,并且有一个内存位置以某种方式指示“数据就绪”。线程1需要在数据存储和存储到“数据已准备就绪”之间至少有一个sfence。线程2需要在表示“数据已准备就绪”的数据加载和数据加载之间保持平衡

如果传输中只涉及常规(非临时、DMA设备等)加载/存储,则只需要编译器防护。此外,带锁前缀的指令意味着围栏。例如,有时“数据准备就绪”位置不仅仅是一个标志,而是一个原子计数器,用于操作它的以锁为前缀的递增/递减可以用作围栏

此模式还包括旋转锁。释放锁是一种“发送”。获取锁是“接收”

模式“共识”:两个线程必须就某事达成共识。必须有一个mfence(或由带锁前缀的指令暗示的mfence)。围栏必须位于“我发布了我的投票”和“我阅读了另一个线程的投票”之间“。就是一个例子。最难的部分是找出这种模式。我们曾经错过了TBB内部的一个深层次的问题,共识问题是“是否抛出了异常?”最终我们意识到这是一个共识问题,因此需要一个MFENE

上面的两种模式是经验法则,并不涵盖所有情况,但我发现它们涵盖了99%的情况。

我的经验(不是在Linux内核中)是,这两种模式涵盖了绝大多数的防护需求

模式“Send/receive”:线程1将数据发送到线程2,并且有一个内存位置以某种方式指示“数据就绪”。线程1需要在数据存储和存储到“数据已准备就绪”之间至少有一个sfence。线程2需要在表示“数据已准备就绪”的数据加载和数据加载之间保持平衡

如果传输中只涉及常规(非临时、DMA设备等)加载/存储,则只需要编译器防护。此外,带锁前缀的指令意味着围栏。例如,有时“数据准备就绪”位置不仅仅是一个标志,而是一个原子计数器,用于操作它的以锁为前缀的递增/递减可以用作围栏

此模式还包括旋转锁。释放锁是一种“发送”。获取锁是“接收”

模式“共识”:两个线程必须就某事达成共识。必须有一个mfence(或由带锁前缀的指令暗示的mfence)。围栏必须位于“我发布了我的投票”和“我阅读了另一个线程的投票”之间“。就是一个例子。最难的部分是找出这种模式。我们曾经错过了TBB内部的一个深层次的问题,共识问题是“是否抛出了异常?”最终我们意识到这是一个共识问题,因此需要一个MFENE

上面的两种模式是经验法则,并不涵盖所有情况,但我发现它们涵盖了99%的情况。

内存屏障不会序列化CPU上的指令,而是序列化CPU外部的内存访问。为了回答您的问题,需要粘贴整个。内存屏障不会序列化CPU上的指令,而是序列化CPU外部的内存访问。要回答您的问题,需要粘贴整个。