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