我如何判断我应该在代码中的什么地方放置内存屏障?

How can I judge where should I put memory barrier in the code?

在读ldd3的时候,我得到了内存屏障的概念,据说代码执行会被重新排序,比如缓存和编译优化等原因。我认为可以重新排序没有依赖关系的代码以获得更好的性能,并且不能优化 IO 端口寄存器,因为它需要包含一致的数据。但是我看不懂下面的代码,请问我应该在什么地方插入像smb(),mb(),barrier()这样的函数?

例如,在示例代码中 short from ldd3.

/*
* Atomicly increment an index into short_buffer
*/
static inline void short_incr_bp(volatile unsigned long *index, int delta)
{
    unsigned long new = *index + delta;
    barrier(); /* Don't optimize these two together */
    *index = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new;
}

barrier前的行和barrier后的行如何重新排序?我认为后者取决于前者先执行以获得 new 值。 这真的让我很困惑。

使用内存屏障代替锁定以获得更好的性能。当内存屏障提供所需的同步时,有几种标准模式。例如,您可以从内核源代码中阅读 Documentation/memory-barriers.txt

在 ldd3 barriers 的给定示例中,使用比平时更狡猾。在当前内核的术语中(与 ldd3 中描述的 2.20+ 相反)可以使用 ACCESS_ONCE() 宏表达相同的意图。

unsigned long new = *index + delta;
ACCESS_ONCE(*index) = (new >= (short_buffer + PAGE_SIZE)) ? short_buffer : new;

没有障碍,编译器可能会分配 *index 两次:

*index = *index + delta;
if(*index > (short_buffer + PAGE_BUFFER)
    *index = short_buffer;

因为*index作为unprotected invariant在多线程中使用(表示哪个缓冲区可用),将中间值*index + delta写入make不变量,被其他线程看到,不正确。 ACCESS_ONCE() 宏阻止了这种情况,它强制编译器仅在明确请求时生成对变量的访问(写入)。

实际上,ACCESS_ONCE(以及您代码中的障碍)对于带有 volatile 修饰符的变量来说是多余的。