谈谈volatile

来源:互联网 发布:淘宝卖家服务热线 编辑:程序博客网 时间:2024/05/01 22:30

转自:http://home.eeworld.com.cn/my/link.php?url=http://bbs.eeworld.com.cn%2Fviewthread.php%3Ftid%3D339278

一直做汇编,做着嵌入式最底层的开发。最近,心血来潮,看起了C语言的书。看的过程中,就想谈谈volatile.

  在谭浩强的C语言书中没有谈到volatile这个类型修饰符。但是,做嵌入式开发,volatile是个极其重要的类型修饰符。
  volatile修饰以下几种类型的变量:
  1> 并行设备的硬件寄存器(如:状态寄存器)。
  2> 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)。
  3> 多线程应用中被几个任务共享的变量。
  为什么会这样呢?我在做汇编的时候,没有这个类型修饰符,也没有说要注意这么多,遇到这几种类型的变量,就要小心处理啊?
  其实这和编译器有关。不是C语言和汇编语言的区别。
  汇编语言是最贴近机器语言的,它的编译器就是将一条一条的指令转换为机器码。在汇编中,一切皆是地址。所以你对某个操作数进行操作时,都是具体到某个内存地址。
  在C语言中,C语言通过编译器编译解释为汇编。这过程中编译器会自作主张。我们来看一个例子:
XBYTE[2] = 0x55;
XBYTE[2] = 0x56;
XBYTE[2] = 0x57;       
XBYTE[2] = 0x58;
  如果对外部硬件,上述四条语句表示不同的操作(机器码不一样),会产生四种不同的动作。那么编译器会对上述四条语句进行优化,只处理XBYTE[2]=0x58,而忽略的前三条语句(即只产生一条机器代码)。
  这样的优化就会对于那些不经意间被操作系统、硬件、其他线程改变的变量是不利的,这些不经意的改变可能是在你的程序本身不知道的情况下改变的。所以,我们用volatile修饰这些变量,以防止编译器去优化,告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存cathe。这样就适应它的未知何时会发生的变化。
  下面,我以“一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)”为例,来说明一下。
  CPU访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
static int i=0;
int main(void)
{
  ......
  while(1)
  {
   if(1) dosomething();
   }       
  }
/* interrupt service routine */
void ISR_2(void)
{
  int i=1;
}
  在嵌入式实时系统中,希望中断处理子程序处理的事情越少越好。所以这里只是在中断发生的时候,有变量i标记一下。在主函数中检测i值变化,再dosomething。
  但是,由于编译器判断在main函数里面没有修改过i,因此,可能只执行一次对从i到某寄存器的读操作,以后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远不会被调用。所以,此时该变量应该加上volatile,使得它每次被使用时,都从具体的存储单元取数。
 其实,上述的“多线程中被几个任务共享的变量”和中断是类似的原理。
 现在来深入分析下中断。我在做汇编的时候,就很有感触的。
 我在写汇编程序时,MAINLOOP中是死循环。中断是定时计数器的中断,在跑MAINLOOP时,TIMER也是在不断的计数的。我把它们想成两个任务,同时在做的。只不过一个是软件在做,一个是硬件在做的。但他们同时会改变某个变量。就应该用volatile修饰。
 写不来了,先写到这。