全部博文(10)
分类: LINUX
2014-07-14 15:48:04
Linux 内存屏障-rmb wmb barrier
2014/6/8 冯健
1. arm-linux中对内存屏障的定义, 在arch/arm/include/asm/barrier.h
2. barrier()宏, 在linux/compiler-gcc.h中
#define barrier() __asm__ __volatile__("": : :"memory")
不用太多的繁琐语言来解释,直接看下面的文字:
CPU越过内存屏障后,将刷新自己对存储器的缓冲状态[借鉴别人的话]。这条语句实际上不生成任何代码,但可使gcc在 barrier()之后刷新寄存器对变量的分配。
也就是说,barrier()宏只约束gcc编译器,不约束运行时的CPU行为。 举例:
1 int a = 5, b = 6;
2 barrier();
3 a = b;
在line 3,GCC不会用存放b的寄存器给a赋值,而是invalidate b的Cache line,重新读内存中的b值,赋值给a。
也就是上述的代码如果用arm汇编写大致是这样的,
mov r1,#5
mov r2,#6
str r1,变量a的地址
str r2,变量b的地址
ldr r1, 变量b的地址
str r1, 变量a的地址
如果没有加barrier(),代码就应该可能是这样的,
mov r1,#5
mov r2,#6
str r1,变量a的地址
str r2,变量b的地址
mov r1,r2
str r1, 变量a的地址
我发现在uncompressed.h有用到这个宏,下面的putc()用于在内核解压阶段向串口输出。
#define UART(x) (*(volatile unsigned long *)(uart_base + (x)))
static void putc(int ch)
{
if (!uart_base)
return;
if (!(UART(UCR1) & UCR1_UARTEN))
return;
//这里就是说每次都需要从硬件寄存器中读取寄存器的值,而不是直接读取arm寄存器中缓存的值,
//因为这里需要不断读取硬件的状态,显然不可以读取arm寄存器中缓存的内容
while (!(UART(USR2) & USR2_TXFE))
barrier();
UART(TXR) = ch;
}
3. rmb() wmb() mb(),在arch/arm/include/asm/barrier.h
#ifdef CONFIG_ARCH_HAS_BARRIERS
#include
#elif defined(CONFIG_ARM_DMA_MEM_BUFFERABLE) || defined(CONFIG_SMP)
#define mb() do { dsb(); outer_sync(); } while (0)
#define rmb() dsb()
#define wmb() do { dsb(st); outer_sync(); } while (0)
#else
#define mb() barrier()
#define rmb() barrier()
#define wmb() barrier()
#endif
可以看见,mb几个宏几乎就是barrier()的封装。rmb()几乎等同于barrier()。wmb()是确保之前的写操作一定在之后的写操作之前完成。
CONFIG_ARCH_HAS_BARRIERS:
This option allows the use of custom mandatory barriers
included via the mach/barriers.h file.
所以CONFIG_ARCH_HAS_BARRIERS可以让用户提供机器特定的barrier代码,而不是arm平台级别的通用barrier。一般来说,肯定都是使用arm平台级通用的barrier,也就是说mb() rmb() wmb() 全部退化为barrier()。
4. 再总结一下
rmb():会强制CPU在读取内存变量A的值时,不是从A的CPU寄存器缓存中去读,而是从内存中A的地址处去读;如果A指的是一个外设寄存器,就从外设寄存器地址处去读,而不是从CPU寄存器缓存中去读。
wmb():确保之前的写操作一定在之后的写操作之前完成。网上的解释都是这句笼统的话。我就不明白,如果:
两个不同的地址值int* p1=xxx,*p2=yyy,那么下面的代码:
int* p1=xxx,*p2=yyy;
1: *p1=20;
2: *p2=10;
barrier()或者wmb();
3: *p1=40;
4: *p2=50;
Wmb()到底是要保证(1、 2)必须早于(3、4)完成;还是保证1必须先于3且2必须先于4;
我觉得,应该是保证对同一个物理地址的写有序把?也就是1必须先于3且2必须先于4。。。。1和4之间乱序无所谓,2和3乱序了也无所谓吧?是这样吗?