一、概念
#define cc_barrier() __asm__ __volatile__("": : :"memory")
int x = 0, y = 0;
void func() {
x = 1;
cc_barrier(); // 确保 x=1 在 y=1 之前执行
y = 1;
}
注:cc_barrier宏的作用域为func函数,确保该宏前面的内容比该宏后面的内容先执行,但不保证该宏前面多段代码的执行顺序,也不保证该宏后面的多端代码的执行顺序。
现在大多数现代计算机编程为了提高性能,使用多线程运行代码而采取乱序执行,这可能会导致程序运行不符合我们预期,内存屏障就是一类同步屏障指令,是CPU或者编译器在对内存随机访问的操作中的一个同步点,只有在此点之前的所有读写操作都执行后才可以执行此点之后的操作。
1.代码解释
1)__asm__用于指示编译器在此插入汇编语句
2)__volatile__用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
3)""
:这里没有实际的汇编指令,所以是空字符串。
4): : : "memory"
:这是约束条件。"memory" 告诉编译器,在这个屏障之前和之后,内存有可能被修改,因此编译器不应重新排序读取或写入内存的指令。
二、内存屏障原理
内存屏障的原理是通过影响编译器和处理器的行为,确保多线程或并发程序中的内存操作按照预期的顺序执行,以维护内存一致性和可见性。
内存屏障通过向编译器和处理器发出指令,确保内存操作的顺序不会被重排,并控制缓存写回的时机,以维护内存的一致性和可见性。这对于多线程或并发程序的正确性至关重要,因为它们需要确保共享数据的正确同步和一致性。内存屏障的具体实现方式会依赖于硬件和编译器的特性。
这样的汇编代码实际上并不执行实际的操作,它只是一种内存屏障的标记,用于告诉编译器和处理器在这个位置执行内存屏障。内存屏障的具体行为是由编译器和处理器来实现的,而不是通过执行汇编指令来完成的。
内存屏障的作用是告诉编译器和处理器在某个点上不要对内存读写操作进行重排。这是因为在多线程或并发程序中,编译器和处理器可能会对代码进行重排,以提高性能。这种重排可能导致多线程程序中的内存操作的顺序与代码中的顺序不一致,从而引发问题。
内存屏障告诉编译器和处理器在内存屏障之前和之后的内存读写操作的顺序不会被重排。这是通过生成一个具有 `"memory"` 参数的内存屏障指令(如 x86 架构的 `mfence` 指令或其他类似的指令)来实现的。这个指令的存在会告诉处理器在执行内存屏障之前和之后的内存操作时,必须按照代码中的顺序执行,不能进行重排。
总之,这样的汇编代码实际上是一种编译器内联汇编的方式,用于生成内存屏障指令,以确保内存操作的顺序不会被重排。它的作用是通过编译器和处理器的行为来实现的,而不是通过执行实际的汇编操作来完成的。这有助于维护多线程程序的内存一致性和可见性。
三、编译器和处理器的处理方法
编译器和处理器是通过特定的机制和策略来实现内存屏障的效果,以确保内存操作的顺序不会被重排。这些机制和策略可以在编译器级别和处理器级别上分别实现。
以下是编译器和处理器如何操作以实现内存屏障效果的概述:
编译器级别:
1. 重排序禁止:编译器在生成机器码时,会遵循编程语言的规范,并尽量保持内存操作的顺序与源代码中的顺序一致。编译器会生成指令以确保在内存屏障之前和之后的内存操作不会被重排。
2. 内存屏障指令生成:编译器会生成适当的内存屏障指令,这些指令会告诉处理器不要对内存操作进行重排。具体的内存屏障指令因体系结构而异,例如,在 x86 架构中,可以使用 `mfence` 指令来实现内存屏障。
处理器级别:
1. 指令重排序控制:现代处理器通常支持指令重排序,以充分利用处理器资源。然而,处理器会遵循内存屏障指令的影响,不会在内存屏障之前和之后对内存操作进行乱序执行。这确保了内存操作的顺序性。
2. 缓存写回控制:处理器内部的缓存(如数据缓存和指令缓存)可能会将修改的数据缓存起来,然后在适当的时候写回主内存。内存屏障可以告诉处理器在执行内存屏障之前和之后,将所有修改的数据写回主内存,以确保数据的一致性。
总之,编译器和处理器通过遵循内存屏障指令和相应的规则,确保在内存屏障之前和之后的内存操作不会被重排,同时控制缓存写回的时机,以维护内存一致性和可见性。这些机制和策略有助于确保多线程程序的正确性和可靠性,特别是在共享数据的情况下。