[英]compiler memory barrier and mutex
posix标准说像mutex这样的东西会强制执行内存同步。 但是,编译器可能会重新排序内存访问。 说我们有
lock(mutex);
setdata(0);
ready = 1;
unlock(mutex);
可能会通过编译器重新排序将其更改为下面的代码,对吗?
ready = 1;
lock(mutex);
setdata(0);
unlock(mutex);
那么互斥量如何同步内存访问? 更确切地说,编译器如何知道重锁不应该在锁定/解锁时发生?
实际上这里对于单线程方面,就绪分配重新排序是完全安全的,因为就绪不用于函数调用锁(mutex)。
编辑:因此,如果函数调用是编译器无法解决的问题,我们可以将其视为编译器内存屏障
asm volatile("" ::: "memory")
一般的答案是,如果你想将它用于POSIX目标,你的编译器应该支持POSIX,而这种支持意味着它应该知道避免重新排序锁定和解锁。
也就是说,这种知识通常是以一种微不足道的方式实现的:编译器不会通过调用可能使用或修改它们的外部函数来重新排序对(不可证实 - 本地)数据的访问。 应该知道关于lock
和unlock
一些特殊内容,以便能够重新排序。
不,它并不是那么简单,因为“对全局函数的调用总是一个编译器障碍” - 我们应该添加“除非编译器知道关于该函数的具体内容”。 确实发生了这种情况:例如Linux上的pthread_self
(NPTL)声明为__const__
属性,允许gcc
在pthread_self()
调用中重新排序,甚至完全消除不必要的调用。
我们可以很容易地想象一个编译器支持获取/释放语义的函数属性,使得lock
和unlock
不是一个完整的编译器障碍。
编译器不会在不清楚它是否安全的情况下重新排序。 在你的“假设”示例中,你没有提出重新排序的内存访问,你问的是编译器是否完全改变了代码排序 - 而且它不会。 编译器可能做的事情是改变实际内存读/写的顺序,但不改变函数调用(有或没有那些内存访问)。
编译器可能重新排序内存访问的示例...假设您有以下代码:
a = *pAddressA;
b = *pAddressB;
并且考虑pAddressB
的值在寄存器中而pAddressA
不在的情况。 编译器首先读取地址B,然后将pAddressA
的值移动到同一个寄存器中以便可以接收新位置,这是公平的游戏。 如果在这些访问之间碰巧发生了函数调用,则编译器无法执行此操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.