volatile關鍵字是給編譯器看的,c語言是一種操作性語言,與硬體底層關係比較密切,尤其是在嵌入式領域。
比如如下程式:
a = 1;
a = 2;
a = 4;
printf("%d\n",a);
經過編譯器優化後可能就變成了:
a = 4;
printf("%d\n",a);
無形中a = 1和 a = 2的操作就被省去了,表面是沒問題,但是在嵌入式中就會出現問題,因為a可能代表乙個片內外設暫存器,賦值1和2是有實際含義的。如果a用volatile進行修飾,編譯器就不會進行優化了。
而 優化做法是,由於編譯器發現兩次從i讀資料的**之間的**沒有對i進行過操作,它會自動把上次讀的資料放在k中。而不是重新從i裡面讀。這樣以來,如果 i是乙個暫存器變數或者表示乙個埠資料就容易出錯,所以說volatile可以保證對特殊位址的穩定訪問,不會出錯。
/**********************
位操作(bit manipulation)
//*********************
嵌入式程式設計中經常用到 volatile這個關鍵字,在網上查了下他的用法可以歸結為以下兩點:
一:告訴compiler不能做任何優化
即使你要compiler做優化,它也不會把兩次付值語句間化為一。它只能做其它的優化。這對device driver程式設計師很有用。
二:表示用volatile定義的變數會在程式外被改變,每次都必須從記憶體中讀取,而不能把他放在cache或暫存器中重複使用。
如果沒有 volatile doother()不會被執行
詳解c中volatile關鍵字
volatile提醒編譯器它後面所定義的變數隨時都有可能改變,因此編譯後的程式每次需要儲存或讀取這個變數的時候,都會直接從變數位址中讀取資料。如 果沒有volatile關鍵字,則編譯器可能優化讀取和儲存,可能暫時使用暫存器中的值,如果這個變數由別的程式更新了的話,將出現不一致的現象。下面舉 例說明。在dsp開發中,經常需要等待某個事件的觸發,所以經常會寫出這樣的程式:
short flag;
void test()
這段程式等待記憶體變數flag的值變為1(懷疑此處是0,有點疑問,)之後才執行do2()。變數flag的值由別的程式更改,這個程式可能是某個硬體中 斷服務程式。例如:如果某個按鈕按下的話,就會對dsp產生中斷,在按鍵中斷程式中修改flag為1,這樣上面的程式就能夠得以繼續執行。但是,編譯器並 不知道flag的值會被別的程式修改,因此在它進行優化的時候,可能會把flag的值先讀入某個暫存器,然後等待那個暫存器變為1。如果不幸進行了這樣的 優化,那麼while迴圈就變成了死迴圈,因為暫存器的內容不可能被中斷服務程式修改。為了讓程式每次都讀取真正flag變數的值,就需要定義為如下形 式:
volatile short flag;
需要注意的是,沒有volatile也可能能正常執行,但是可能修改了編譯器的優化級別之後就又不能正常執行了。因此經常會出現debug版本正常,但是 release版本卻不能正常的問題。所以為了安全起見,只要是等待別的程式修改某個變數的話,就加上volatile關鍵字。
volatile的本意是「易變的」
由於訪問暫存器的速度要快過ram,所以編譯器一般都會作減少訪問外部ram的優化。比如:
static int i=0;
int main(void) }
/* interrupt service routine. */
void isr_2(void)
程式的本意是希望isr_2中斷產生時,在main當中呼叫do_something函式,但是,由於編譯器判斷在main函式裡面沒有修改過i,因此可 能只執行一次對從i到某暫存器的讀操作,然後每次if判斷都只使用這個暫存器裡面的「i副本」,導致do_something永遠也不會被呼叫。如果變數 加上volatile修飾,則編譯器保證對此變數的讀寫操作都不會被優化(肯定執行)。此例中i也應該如此說明。
一般說來,volatile用在如下的幾個地方:
1、中斷服務程式中修改的供其它程式檢測的變數需要加volatile;
2、多工環境下各任務間共享的標誌應該加volatile;
3、儲存器對映的硬體暫存器通常也要加volatile說明,因為每次對它的讀寫都可能由不同意義;
另外,以上這幾種情況經常還要同時考慮資料的完整性(相互關聯的幾個標誌讀了一半被打斷了重寫),在1中可以通過關中斷來實現,2中可以禁止任務排程,3中則只能依靠硬體的良好設計了。
二、volatile 的含義
作用相同,但一條指令反而不如三條指令快。
三、編譯器優化 → c關鍵字volatile → memory破壞描述符zz
如果彙編指令修改了記憶體,但是gcc 本身卻察覺不到,因為在輸出部分沒有描述,此時就需要在修改描述部分增加「memory」,告訴gcc 記憶體已經被修改,gcc 得知這個資訊後,就會在這段指令之前,插入必要的指令將前面因為優化cache 到暫存器中的變數值先寫回記憶體,如果以後又要使用這些變數再重新讀取。
使用「volatile」也可以達到這個目的,但是我們在每個變數前增加該關鍵字,不如使用「memory」方便。
對volatile關鍵字的理解
來看下面這樣乙個例子 public class threadtest backgroundthread.start timeunit.seconds.sleep 1 stoprequested true 我們希望backgroundthread 執行緒能夠在睡眠一秒後停止,但是實際情況是執行緒陷入了...
對volatile關鍵字的理解
如果volatile變數與普通變數發 了重排序,雖然volatile變數能保證記憶體可 性,但是可能導致普通變數讀取錯誤 jvm通過記憶體屏障來實現限制處理器的重排序。編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序 編譯器選擇了 個 較保守的jmm記憶體屏障插 策略...
Volatile關鍵字理解
物理角度 由於計算機的儲存裝置和cpu的運算速度有幾個數量級的差距,所以現代計算機系統加入一層速度接近 cpu的快取記憶體 cache 但cache帶來乙個問題 快取一致性問題 在多處理器系統中,每個處理器機油自己的cache 工作記憶體 又共享同一主記憶體。舉例 當程式在執行過程中,會將運算需要的...