内核同步机制-优化屏障和内存屏障

优化屏障

编译器编译源代码时,会将源代码进行优化,将源代码的指令进行重排序,以适合于CPU的并行执行。然而,内核同步必须避免指令重新排序,优化屏障(Optimization barrier)避免编译器的重排序优化操作,保证编译程序时在优化屏障之前的指令不会在优化屏障之后执行。
Linux用宏barrier实现优化屏障,gcc编译器的优化屏障宏定义列出如下(在include/linux/compiler-gcc.h中): 

内核同步机制-优化屏障和内存屏障#define barrier() __asm__ __volatile__("": : :"memory")

上述定义中,“__asm__”表示插入了汇编语言程序,“__volatile__”表示阻止编译器对该值进行优化,确保变量使用了用户定义的精确地址,而不是装有同一信息的一些别名。“memory”表示指令修改了内存单元。


内存屏障

软件可通过读写屏障强制内存访问次序。读写屏障像一堵墙,所有在设置读写屏障之前发起的内存访问,必须先于在设置屏障之后发起的内存访问之前完成,确保内存访问按程序的顺序完成。
读写屏障通过处理器构架的特殊指令mfence(内存屏障)、lfence(读屏障)和sfence(写屏障)完成,见《x86-64构架规范》一章。另外,在x86-64处理器中,对硬件进行操作的汇编语言指令是“串行的”,也具有内存屏障的作用,如:对I/O端口进行操作的所有指令、带lock前缀的指令以及写控制寄存器、系统寄存器或调试寄存器的所有指令(如:cli和sti)。
Linux内核提供的内存屏障API函数说明如表2。内存屏障可用于多处理器和单处理器系统,如果仅用于多处理器系统,就使用smp_xxx函数,在单处理器系统上,它们什么都不要。
表2 内存屏障API函数说明

内存屏障的宏定义 功能说明
mb() 适用于多处理器和单处理器的内存屏障。
rmb() 适用于多处理器和单处理器的读内存屏障。
wmb() 适用于多处理器和单处理器的写内存屏障。
smp_mb() 适用于多处理器的内存屏障。
smp_rmb() 适用于多处理器的读内存屏障。
smp_wmb() 适用于多处理器的写内存屏障。

适合于多处理器和单处理器的内存屏障宏定义列出如下(在include/asm-x86/system.h中): 

内核同步机制-优化屏障和内存屏障#ifdef CONFIG_X86_32
内核同步机制-优化屏障和内存屏障/*指令“lock; addl $0,0(%%esp)”表示加锁,把0加到栈顶的内存单元,该指令操作本身无意义,但这些指令起到内存屏障的作用,让前面的指令执行完成。具有XMM2特征的CPU已有内存屏障指令,就直接使用该指令*/
内核同步机制-优化屏障和内存屏障    #define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
内核同步机制-优化屏障和内存屏障    #define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2)
内核同步机制-优化屏障和内存屏障    #define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
内核同步机制-优化屏障和内存屏障#else
内核同步机制-优化屏障和内存屏障    #define mb() asm volatile("mfence":::"memory")
内核同步机制-优化屏障和内存屏障    #define rmb() asm volatile("lfence":::"memory")
内核同步机制-优化屏障和内存屏障    #define wmb() asm volatile("sfence" ::: "memory")
内核同步机制-优化屏障和内存屏障#endif
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障/*刷新后面的读所依赖的所有挂起读操作,在x86-64构架上不需要*/
内核同步机制-优化屏障和内存屏障#define read_barrier_depends() do { } while (0)

宏定义ead_barrier_depends()刷新后面的读所依赖的所有挂起读操作,后面的读操作依赖于正处理的读操作返回的数据。在x86-64构架上不需要此宏。它表明:在此屏障之前,没有来自内存区域数据所依赖的读曾经重排序。所有的读操作处理此原语,保证在跟随此原语的任何读操作此原语之前访问内存(但不需要其他CPU的cache)。此原语在大多数CPU上有比rmb()更轻的份量。
本地CPU和编译器遵循内存屏障的排序限制,仅内存屏障原语保证排序,即使数据有依赖关系,也不能保证排序。例如:下面代码将强迫排序,因为*q的读操作依赖于p的读操作,并且这两个读操作被read_barrier_depends()分开。在CPU 0和CPU 1上执行的程序语句分别列出如下:

内核同步机制-优化屏障和内存屏障CPU 0                                      CPU 1 
内核同步机制-优化屏障和内存屏障b = 2;
内核同步机制-优化屏障和内存屏障memory_barrier();
内核同步机制-优化屏障和内存屏障p = &b;                                      q = p;
内核同步机制-优化屏障和内存屏障                                                read_barrier_depends();
内核同步机制-优化屏障和内存屏障                                                d = *q;


下面的代码没有强制排序,因为在a和b的读操作之间没有依赖关系,因此,在一些CPU上,如:Alpha,y将设置为3,x设置为0。类似这种没有数据依赖关系的读操作,需要排序应使用rmb()。

内核同步机制-优化屏障和内存屏障CPU 0                                        CPU 1
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障a = 2;
内核同步机制-优化屏障和内存屏障memory_barrier();
内核同步机制-优化屏障和内存屏障b = 3;                                         y = b;
内核同步机制-优化屏障和内存屏障                                                 read_barrier_depends();
内核同步机制-优化屏障和内存屏障                                                 x = a;


 

适合于多处理器的内存屏障宏定义列出如下(在include/asm-x86/system.h中): 

内核同步机制-优化屏障和内存屏障#ifdef CONFIG_SMP
内核同步机制-优化屏障和内存屏障    #define smp_mb() mb()
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障    #ifdef CONFIG_X86_PPRO_FENCE
内核同步机制-优化屏障和内存屏障        # define smp_rmb() rmb()
内核同步机制-优化屏障和内存屏障    #else
内核同步机制-优化屏障和内存屏障        # define smp_rmb() barrier()
内核同步机制-优化屏障和内存屏障    #endif
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障    #ifdef CONFIG_X86_OOSTORE
内核同步机制-优化屏障和内存屏障        # define smp_wmb() wmb()
内核同步机制-优化屏障和内存屏障    #else
内核同步机制-优化屏障和内存屏障        # define smp_wmb() barrier()
内核同步机制-优化屏障和内存屏障    #endif
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障
内核同步机制-优化屏障和内存屏障    #define smp_read_barrier_depends() read_barrier_depends()
内核同步机制-优化屏障和内存屏障    #define set_mb(var, value) do { (void)xchg(&var, value); } while (0)
内核同步机制-优化屏障和内存屏障#else
内核同步机制-优化屏障和内存屏障    #define smp_mb() barrier()
内核同步机制-优化屏障和内存屏障    #define smp_rmb() barrier()
内核同步机制-优化屏障和内存屏障    #define smp_wmb() barrier()
内核同步机制-优化屏障和内存屏障    #define smp_read_barrier_depends() do { } while (0)
内核同步机制-优化屏障和内存屏障    #define set_mb(var, value) do { var = value; barrier(); } while (0)
内核同步机制-优化屏障和内存屏障#endif

 

函数rdtsc_barrier用于加内存屏障阻止RDTSC猜测,当在一个定义的代码区域使用读取时间戳计数器(Read Time-Stamp Counter,RDTSC)函数(或者函数get_cycles或vread)时,必须加内存屏障阻止RDTSC猜测。其列出如下:

内核同步机制-优化屏障和内存屏障static inline void rdtsc_barrier(void)
内核同步机制-优化屏障和内存屏障{
内核同步机制-优化屏障和内存屏障    alternative(ASM_NOP3, "mfence", X86_FEATURE_MFENCE_RDTSC);
内核同步机制-优化屏障和内存屏障    alternative(ASM_NOP3, "lfence", X86_FEATURE_LFENCE_RDTSC);
内核同步机制-优化屏障和内存屏障}
上一篇:数字营销行业大数据平台云原生升级实战


下一篇:苏州“医智汇”医疗器械创新创业大赛疑问解析(二)