Volatile 字面的意思時(shí)易變的,不穩(wěn)定的。編譯器在優(yōu)化代碼時(shí),可能會(huì)把經(jīng)常用到的代碼存在Cache里面,然后下一次調(diào)用就直接讀取Cache而不是內(nèi)存,這樣就大大提高了效率。但是問題也隨之而來了。
本文引用地址:http://m.butianyuan.cn/article/201611/322064.htm在多線程程序中,如果把一個(gè)變量放入Cache后,又有其他線程改變了變量的值,那么本線程是無法知道這個(gè)變化的。它可能會(huì)直接讀Cache里的數(shù)據(jù)。但是很不幸,Cache里的數(shù)據(jù)已經(jīng)過期了,讀出來的是不合時(shí)宜的臟數(shù)據(jù)。這時(shí)就會(huì)出現(xiàn)bug。也就是說如果它是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)就容易出錯(cuò),所以說volatile可以保證對特殊地址的穩(wěn)定訪問。
用Volatile聲明變量可以解決這個(gè)問題。用Volatile聲明的變量就相當(dāng)于告訴編譯器,我不要把這個(gè)變量寫Cache,因?yàn)檫@個(gè)變量是可能發(fā)生改變的。
如果一個(gè)變量的值可能會(huì)被程序操作之外的其它操作所改變,那么你必需用volatile 聲明。在嵌入式系統(tǒng)中其它操作是:中斷服務(wù)程序的操作、硬件動(dòng)作的操作等,下面列舉了需要加volatile 聲明的情況。
1、中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;
3、存儲(chǔ)器映射的硬件寄存器通常也要加volatile說明,因?yàn)槊看螌λ淖x寫都可能由不同意義;
用volatile聲明的變量是不會(huì)被編譯器優(yōu)化掉的
例如:
#definePortA( * ( volatile unsignedint * )0x0000 )
這樣 PortA 成為一個(gè)地址在0x0000的unsigned int類型變量。這個(gè)定義看起來很復(fù)雜,其實(shí)它也可以分解成幾個(gè)很簡單的部分來看。 ( volatile unsignedint * )是C語言中的強(qiáng)制類型轉(zhuǎn)換,它的作用是把0x0000這個(gè)純粹的十六進(jìn)制數(shù)轉(zhuǎn)換成為一個(gè)(地址)指針,其中volatile并不是必要的,它只是告訴編譯器,這個(gè)值與外界環(huán)境有關(guān),不要對它優(yōu)化。接下來在外面又加了一個(gè)*號,就表示0x0000內(nèi)存單元中的內(nèi)容了。經(jīng)過這個(gè)宏定義之后,PortA就被可以做為一個(gè)普通的變量來操作,所有出現(xiàn)PortA的地方編譯的時(shí)候都被替換成( * ( volatile unsignedint * )0x0000 ),外面一層括號是為了保證里面的操作不會(huì)因?yàn)檫\(yùn)算符優(yōu)先級或者其它不可預(yù)測的原因被改變而無法得到預(yù)期的結(jié)果。
PORTA做為一個(gè)輸入端口,其值是由外部設(shè)備決定的,由于外部設(shè)備的變化是隨機(jī)的,因此第一次讀取的值和第二次讀取的值很可能不同,所以我們把它聲明為volatile變量。
a = PORTA;
a = PORTA;
由于PORTA是用volatile聲明的變量,編譯器不會(huì)把它優(yōu)化成一句,而如果不是volatile聲明的編譯器就會(huì)將第二句優(yōu)化掉,從而程序?qū)?huì)忽略輸入端口的變化。
通常把嵌入式設(shè)備的所有外圍器件寄存器都聲明為volatile的。
這種定義方法適合所有的C編譯器,可移植性好,但PortA并不是一個(gè)真正的變量,只是一個(gè)宏名,當(dāng)你調(diào)試一個(gè)程序的時(shí)候,無法在調(diào)試窗口觀察它的值。另外連接器也失去了靈活性,它得防止其它變量跟此變量沖突。
評論