Chinaunix首页 | 论坛 | 博客
  • 博客访问: 75788
  • 博文数量: 10
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 321
  • 用 户 组: 普通用户
  • 注册时间: 2014-05-24 18:56
文章分类
文章存档

2019年(2)

2014年(8)

我的朋友

分类: 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 3GCC不会用存放b的寄存器给a赋值,而是invalidate bCache 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的值时,不是从ACPU寄存器缓存中去读,而是从内存中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)必须早于(34)完成;还是保证1必须先于32必须先于4

我觉得,应该是保证对同一个物理地址的写有序把?也就是1必须先于32必须先于4。。。。14之间乱序无所谓,23乱序了也无所谓吧?是这样吗?

阅读(2568) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~