volatile的使用

来源:互联网 发布:微信怎么在淘宝付款 编辑:程序博客网 时间:2024/06/08 09:31

volatile的使用

在嵌入式中每次都要使用volatile关键字。

#define GPBCON      (*(volatile unsigned long *)0x56000010)
#define GPBDAT      (*(volatile unsigned long *)0x56000014)

volatile的本意是“易变的”(volatile应该解释为“直接存取原始内存地址”比较合适“易变的”这种解释简直有点误导人)

 因为访问寄存器要比访问内存单元快的多,所以编译器一般都会作减少存取内存的优化。

当要求使用volatile声明变量值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。

遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用valatile,则编译器将对所声明的语句进行优化。

举例如下

vim test.c

/* artificial device registers */
unsigned char recv;
unsigned char send;
/* memory buffer */
unsigned char buf[3];
int main(void)
{
        buf[0] = recv;
        buf[1] = recv;
        buf[2] = recv;
        send = ~buf[0];
        send = ~buf[1];
        send = ~buf[2];
        return 0;
}

编译 gcc tes.c

反汇编 objdump -d a.out

 

 buf[0] = recv;
 80483a2:       0f b6 05 19 a0 04 08   
movzbl 0x804a019,%eax
 80483a9:       a2 1a a0 04 08          mov    %al,0x804a01a
        buf[1] = recv;
 80483ae:       0f b6 05 19 a0 04 08   
movzbl 0x804a019,%eax
 80483b5:       a2 1b a0 04 08          mov    %al,0x804a01b
        buf[2] = recv;
 80483ba:       0f b6 05 19 a0 04 08    movzbl 0x804a019,%eax
 80483c1:       a2 1c a0 04 08          mov    %al,0x804a01c
        send = ~buf[0];
 80483c6:       0f b6 05 1a a0 04 08    movzbl 0x804a01a,%eax
 80483cd:       f7 d0                   not    %eax
 80483cf:       a2 18 a0 04 08          mov    %al,0x804a018
        send = ~buf[1];
 80483d4:       0f b6 05 1b a0 04 08    movzbl 0x804a01b,%eax
 80483db:       f7 d0                   not    %eax
 80483dd:       a2 18 a0 04 08          mov    %al,0x804a018
        send = ~buf[2];
 80483e2:       0f b6 05 1c a0 04 08    movzbl 0x804a01c,%eax
 80483e9:       f7 d0                   not    %eax
 80483eb:       a2 18 a0 04 08          mov    %al,0x804a018

 

优化编译 gcc   -O  test.c

反汇编 objdump -d a.out

 

   buf[0] = recv;
 80483ae:       0f b6 05 19 a0 04 08    movzbl 0x804a019,%eax
 80483b5:       a2 1a a0 04 08          mov    %al,0x804a01a
        buf[1] = recv;
 80483ba:       a2 1b a0 04 08          mov    %al,0x804a01b
        buf[2] = recv;
 80483bf:       a2 1c a0 04 08          mov    %al,0x804a01c
        send = ~buf[0];
        send = ~buf[1];
        send = ~buf[2];
 80483c4:       f7 d0                   not    %eax
 80483c6:       a2 18 a0 04 08          mov    %al,0x804a018

 

与前面的相比 缺少了 movzbl 0x804a019,%eax  对内存的读取省略了。

只有第一条语句从内
存地址0x804a019 读一个字节到寄存器eax 中,然后从寄存器al保存到buf[0] ,后两条语句就不再
从内存地址0x804a019 读取,而是直接把寄存器al的值保存到buf[1] 和buf[2]

因为设备寄存器往往具有以下特性:
1 设备寄存器中的数据不需要改写就可以自己发生变化,每次读上来的值都可能不一样。
2  连续多次向设备寄存器中写数据并不是在做无用功,而是有特殊意义的。
用优化选项编译生成的指令明显效率更高,但使用不当会出错,为了避免编译器自作聪明,把不该
优化的也优化了,程序员应该明确告诉编译器哪些内存单元的访问是不能优化的,在C 语言中可以
用volatile 限定符修饰变量,就是告诉编译器,即使在编译时指定了优化选项,每次读这个变量仍
然要老老实实从内存读取,每次写这个变量也仍然要老老实实写回内存,不能省略任何步骤。我们
把代码的开头几行改成:
/* artificial device registers */
volatile unsigned char recv;

volatile unsigned char send