考虑一下这个C代码:
extern volatile int hardware_reg;
void f(const void *src, size_t len)
{
void *dst = <something>;
hardware_reg = 1;
memcpy(dst, src, len);
hardware_reg = 0;
}memcpy()调用必须发生在两个赋值之间。一般来说,由于编译器可能不知道被调用的函数将做什么,所以它不能在分配之前或之后重新排序对函数的调用。但是,在这种情况下,编译器知道函数将做什么(甚至可以插入内联内置替代程序),并且可以推断memcpy()永远无法访问hardware_reg。在这里,在我看来,如果编译器想要移动memcpy()调用,它就不会遇到麻烦。
因此,问题是:单独的函数调用是否足以发出防止重新排序的内存屏障,或者是在调用memcpy()之前和之后需要的显式内存屏障?
如果我误会了,请纠正我。
发布于 2011-04-17 23:36:39
编译器不能在memcpy()之前或hardware_reg = 0之后重新排序--这是volatile将确保的--至少在编译器发出的指令流中是这样的。函数调用不一定是“内存屏障”,但它是序列点。
C99标准提到了volatile (5.1.2.3/5“程序执行”):
在序列点的
中,易失性对象是稳定的,因为以前的访问已经完成,而后续的访问还没有发生。
因此,在memcpy()表示的序列点,必须发生写入1的易失性访问,而不能发生写入0的易失性访问。
然而,有两件事我想指出:
<something>是什么,如果对目标缓冲区不做任何其他操作,编译器可能能够完全删除memcpy()操作。这就是微软提出SecureZeroMemory()函数的原因。away.volatile不一定意味着内存障碍(这是硬件问题,而不仅仅是代码排序问题),因此如果您在多进程计算机或某些类型的硬件上运行,可能需要显式调用内存屏障(可能是Linux上的wmb() )。从MSVC 8 (VS 2005)开始,Microsoft记录了volatile关键字暗示了适当的内存屏障,因此可能不需要单独的特定内存屏障调用:。
- [http://msdn.microsoft.com/en-us/library/12a04hfd.aspx](http://msdn.microsoft.com/en-us/library/12a04hfd.aspx)此外,在优化时,编译器必须保持对易失性对象的引用以及对其他全局对象的引用之间的顺序。特别地,
- A write to a volatile object (volatile write) has Release semantics; a reference to a global or static object that occurs before a write to a volatile object in the instruction sequence will occur before that volatile write in the compiled binary.
- A read of a volatile object (volatile read) has Acquire semantics; a reference to a global or static object that occurs after a read of volatile memory in the instruction sequence will occur after that volatile read in the compiled binary.发布于 2011-04-17 11:45:06
据我所知你的推理
编译器在移动
memcpy调用时不会遇到麻烦
是对的。语言定义没有回答您的问题,只能参考特定的编译器来解决。
很抱歉没有更多有用的信息。
发布于 2011-04-17 11:51:14
我的假设是,编译器永远不会重新排序易失性赋值,因为它必须假设它们必须在代码中发生的位置执行。
https://stackoverflow.com/questions/5693274
复制相似问题