volatile这个关键字的字面意义是“暂时的、不稳定的”,主要是告诉编译器“我当前定义的变量是一个特殊的多线程变量,你不要按照你的方式给我优化它”。

在当前我接触的程序中,主要是给多线程共享的变量加上该关键字。

近一步的了解发现,该关键字还有其他的两种使用情况:

1、中断服务程序中修改的供其它程序检测的变量也需要加

2、存储器映射的硬件寄存器通常也要加。

第一种情况需要加volatile的原因和多线程共享变量的原因是一致的,都是避免线程去访问没有被更新的cpu寄存器中的数据,导致的数据的不一致性。加上该关键字后,每次对变量的访问,都要去该变量所在的速度比较慢的内存设备进行访问,而且该变量的值发生变化时,要强迫线程把该变量回写到内存设备。

第二种情况,是因为如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,加上volatile就可以避免编译器优化掉对寄存器进行读写操作的代码,因为每次对它的读写都可能有不同的意义。

####################典型问题######################

关键字volatile有什么含意?并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否真是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

************************补充*******************************

volatile的本意是“易变的 暂态的” 由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:

static int i=0; //应该为static volatile int i=0;

int main(void)
{
  ...
  while (1)
  {
    if (i)   dosomething();
  }
}

/* Interrupt service routine. */
void ISR_2(void)
{
  i=1;
}

程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。
如果将将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。

一般说来,volatile用在如下的几个地方:

1、中断服务程序中修改的供其它程序检测的变量需要加volatile;

2、多任务环境下各任务间共享的标志应该加volatile;

3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

由于使用volatile屏蔽掉了编译器中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

posted on 2010-01-07 23:37  thymon  阅读(545)  评论(0编辑  收藏  举报