分享牛人就是的volatilekeyword

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;
int main(void)
{
...
while (1)
{
if (i) dosomething();
}
}

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说明。由于每次对它的读写都可能由不允许义;
另外,以上这几种情况常常还要同一时候考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中能够通过关中断来实
现,2中能够禁止任务调度。3中则仅仅能依靠硬件的良好设计了。
volatile 的含义
volatile总是与优化有关。编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使用、在哪里失效。分析结果能够用于常量合并,常量传播等优化,进一步能够死代码消除。但有时这些优化不是程序所须要的,这时能够用volatilekeyword禁止做这些优化,volatile的字面含义是易变的,它有以下的作用:
1 不会在两个***作之间把volatile变量缓存在寄存器中。在多任务、中断、甚至setjmp环境下。变量可能被其它的程序改变。编译器自己无法知道,volatile就是告诉编译器这样的情况。
2 不做常量合并、常量传播等优化,所以像以下的代码:
volatile int i = 1;
if (i > 0) ...
if的条件不会当作无条件真。
3 对volatile变量的读写不会被优化掉。假设你对一个变量赋值但后面没用到。编译器经常能够省略那个赋值***作,然而对Memory Mapped IO的处理是不能这样优化的。
前面有人说volatile能够保证对内存操作的原子性。这样的说法不大准确,其一,x86须要LOCK前缀才干在SMP下保证原子性。其二,RISC根本不能对内存直接运算,要保证原子性得用别的方法,如atomic_inc。


对于jiffies,它已经声明为volatile变量。我觉得直接用jiffies++就能够了,不是必需用那种复杂的形式。由于那样也不能保证原子性。
================================================================================
关键在于两个地方:      
  
1. 编译器的优化  (请高手帮我看看以下的理解)
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时。就直接从寄存器中取值;
当变量值在本线程里改变时,会同一时候把变量的新值copy到该寄存器中。以便保持一致
当变量在因别的线程等而改变了值,该寄存器的值不会对应改变。从而造成应用程序读取的值和实际的变量值不一致
当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致  
举一个不太准确的样例:  
发薪资时,会计每次都把员工叫来登记他们的银行卡号;一次会计为了省事,没有即时登记,用了曾经登记的银行卡号。刚好一个员工的银行卡丢了。已挂失该银行卡号;从而造成该员工领不到工资  
员工 -- 原始变量地址  
银行卡号 -- 原始变量在寄存器的备份  
2. 在什么情况下会出现(如1楼所说)
    1). 并行设备的硬件寄存器(如:状态寄存器)  
    2). 一个中断服务子程序中会訪问到的非自己主动变量(Non-automatic variables)  
    3). 多线程应用中被几个任务共享的变量  
     
补充: volatile应该解释为“直接存取原始内存地址”比較合适,“易变的”这样的解释简直有点误导人。  
“易变”是由于外在因素引起的,象多线程,中断等,并非由于用volatile修饰了的变量就是“易变”了,假如没有外因。即使用volatile定义,它也不会变化;
而用volatile定义之后,事实上这个变量就不会因外因而变化了,能够放心使用了;大家看看前面那种解释(易变的)是不是在误导人

 

分享牛人解释的volatilekeyword分享牛人解释的volatilekeyword分享牛人解释的volatilekeyword

 

另外补充volatile在DSP中的理解:

该单词的意思是可变的,易变的。在DSP中,一些寄存器的值的变化有两种情况:(1)硬件上导致的变化。比如中断、ADC等;(2)软件上的变化,比如对某个变量赋值等。

当增加了keywordvolatile。则表示该变量的值可因上述两种情况而发生变化;即,对软件来说,硬件上变化的值是不可预知的,增加了该keyword,提示编译器每次读取该变量时。都要直接读取该变量地址中的寄存器。保证了数据的正确性。

posted @ 2017-07-29 08:51  yangykaifa  阅读(184)  评论(0编辑  收藏  举报