记忆障碍如何运作?

xml*_*lmx 4 c c++ assembly execution instructions

在Windows下,有三个编译器内部函数来实现内存屏障:

1. _ReadBarrier;

2. _WriteBarrier;

3. _ReadWriteBarrier;
Run Code Online (Sandbox Code Playgroud)

但是,我发现了一个奇怪的问题:_ReadBarrier似乎是一个无效的虚函数!以下是VC++ 2012生成的汇编代码.

我的问题是:如何在汇编指令中实现内存屏障功能?

int main()
{   
013EEE10  push        ebp  
013EEE11  mov         ebp,esp  
013EEE13  sub         esp,0CCh  
013EEE19  push        ebx  
013EEE1A  push        esi  
013EEE1B  push        edi  
013EEE1C  lea         edi,[ebp-0CCh]  
013EEE22  mov         ecx,33h  
013EEE27  mov         eax,0CCCCCCCCh  
013EEE2C  rep stos    dword ptr es:[edi]  
    int n = 0;
013EEE2E  mov         dword ptr [n],0  
    n = n + 1;
013EEE35  mov         eax,dword ptr [n]  
013EEE38  add         eax,1  
013EEE3B  mov         dword ptr [n],eax  
    _ReadBarrier();
    n = n + 1;
013EEE3E  mov         eax,dword ptr [n]  
013EEE41  add         eax,1  
013EEE44  mov         dword ptr [n],eax 
}
013EEE56  xor         eax,eax  
013EEE58  pop         edi  
013EEE59  pop         esi  
013EEE5A  pop         ebx  
013EEE5B  add         esp,0CCh  
013EEE61  cmp         ebp,esp  
013EEE63  call        __RTC_CheckEsp (013EC3B0h)  
013EEE68  mov         esp,ebp  
013EEE6A  pop         ebp  
013EEE6B  ret 
Run Code Online (Sandbox Code Playgroud)

MSN*_*MSN 8

_ReadBarrier,_WriteBarrier_ReadWriteBarrier影响编译器如何重新排序代码的内在函数 ; 它们与CPU内存屏障完全无关,仅对特定类型的内存有效(请参阅此处的 "受影响的内存" ).

MemoryBarrier()是用来强制CPU内存屏障的内在函数.但是,Microsoft的建议是继续使用std::atomic<T>VC++.


Mat*_*son 5

现代处理器能够在实际“完成”指令之前执行相当长的指令,因此在涉及某些类型的内存操作(严格排序)时,使用内存屏障来防止其运行得太早。必需的 - 对于大多数情况,如果在变量 b 之前写入变量 a,或者在 a 之前写入 b,实际上并不重要。但有时确实如此。

x86 指令集有lfencesfencefence,它们分别是“隔离”加载、存储和所有内存操作的指令。关于“栅栏”或“屏障”指令的要点是确保屏障指令之前的所有指令在屏障之后的下一条指令可以继续之前完成其加载、存储或两者。

如果您要实现信号量、互斥体或类似指令,这一点很重要,因为在继续读取其他数据之前,存储表示“我已锁定信号量”的值非常重要。比如说,否则事情可能会出错。

请注意,除非您真的知道您在使用内存屏障做什么,否则最好不要使用它们 - 并依赖解决相同问题的现有代码 -std::atomic是资助此类代码的一个地方。我已经编写了相当多的“棘手”代码,但只有一两次我的代码中需要内存屏障。

有几次,我需要让编译器不传播代码,您可以使用“无操作函数”来做到这一点,显然现在甚至有特殊的内部函数可以做到这一点。