linux中使用volatile(附加變數型別)
1 變數型別
1.1 自動變數特點
(1)自動變數就是在函式內部定義的變數,只在內部有效
(2)所以允許其他函式定義相同的變數
(3)他們都有自己的區域性,沒有連線性
(4)自動變數分配記憶體就壓棧,函式返回時退棧
(5)一般不作專門說明的區域性變數,均是自動變數,比如static說明
(6)自動變數使用棧機制使用記憶體
1.2 靜態變數static
1.2.1 區域性靜態變數
(1)記憶體中的位置:靜態儲存區
(2)未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)
(3)當static用來修飾區域性變數的時候,它就改變了區域性變數的儲存位置,從原來的棧中存放改為靜態儲存區,但是區域性靜態變數在離開作用域之後,並沒有被銷燬,而是仍然駐留在記憶體當中,值也保留,等程式下次再去執行這個函式,去使用它
(4)當static用來修飾全域性變數的時候,它就改變了全域性變數的作用域(在宣告他的檔案之外是不可見的),但是沒有改變它的存放位置,還是在靜態儲存區中。
1.2.2 全域性靜態變數
(1)不會被其他檔案所訪問,修改
(2)其他檔案中可以使用相同名字的變數,不會發生衝突
(3)記憶體中的位置:靜態儲存區(靜態儲存區在整個程式執行期間都存在)
(4)未經初始化的全域性靜態變數會被程式自動初始化為0(自動物件的值是任意的,除非他被顯示初始化)
1.2.3 靜態函式
(1)其他檔案中可以定義相同名字的函式,不會發生衝突
(2)靜態函式不能被其他檔案所用。
1.3 暫存器變數
(1)C語言中可以使用暫存器來優化程式效能,將一個常用變數宣告成暫存器變數:resgister int i;如果可能的話,編譯器會為他分配一個單獨額暫存器,那函式執行期間對這個變數的操作全是對暫存器操作,就不用頻繁去訪問記憶體,讀來讀去,自然提高了程式的效能
1.4 易失變數:volatile
(1)表示變數是易失的,易變的
(2)強制訪問記憶體操作,防止編譯器去優化,告訴編譯器每次必須去記憶體中取值,而不是暫存器或快取中
1.5 非自動變數有兩種 一種是全域性變數,一種是靜態變數
2 細說volatile
前言:一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份
2.1 volatile的定義與解釋(對理解它很重要)cache的作用
(1)volatile關鍵字是一種型別修飾符,用它宣告的型別變量表示可以被某些編譯器未知的因素更改,比如:作業系統、硬體或者其它執行緒等。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的程式碼就不再進行優化,從而可以提供對特殊地址的穩定訪問。
(2)volatile 指出 i是隨時可能發生變化的,每次使用它的時候必須從i的地址中讀取,因而編譯器生成的彙編程式碼會重新從i的地址讀取資料放在b中。而優化做法是,由於編譯器發現兩次從i讀資料的程式碼之間的程式碼沒有對i進行過操作,它會自動把上次讀的資料放在b中。而不是重新從i裡面讀。這樣以來,如果i是一個暫存器變數或者表示一個埠資料就容易出錯,所以說volatile可以保證對特殊地址的穩定訪問。
(3)volatile對應的變數可能在你的程式本身不知道的情況下發生改變 對於volatile型別的變數,系統每次用到他的時候都是直接從對應的記憶體當中提取,而不會利用cache當中的原有數值,以適應它的未知何時會發生的變化,系統對這種變數的處理不會做優化——顯然也是因為它的數值隨時都可能變化的情況。
2.2 常使用到volatile的變數
(1)並行裝置的硬體暫存器(如:狀態暫存器)
(2)一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)
(3)多執行緒應用中被幾個任務共享的變數
2.3 三個大問題
(1)一個引數既可以是const還可以是volatile嗎?
解釋:一個例子是隻讀的狀態暫存器。它是volatile因為它可能被意想不到地改變,是內部機制變化造成,而不是你的程式改變了它,而是作業系統把你的程式轉成組合語言時編譯器另外加的彙編改變了它,它是const因為你寫的程式部分不應該試圖去修改它
(2)一個指標可以是volatile嗎?
解釋:可以,儘管不常見,不如在中斷服務子程式修改一個指向一個buffer的指標,防止編譯器去優化,告訴編譯器每次必須去記憶體中取值,而不是暫存器或快取中
(3)多執行緒應用總被幾個任務共享的變數
解釋:volatile對應的變數可能在你的程式本身不知道的情況下發生改變 。比如多執行緒的程式,共同訪問的記憶體當中,多個程式都可以操縱這個變數 ,你自己的程式,是無法判定合適這個變數會發生變化 ,就像作業系統對執行緒的排程,內部排程機制在處理,所以有些變數程式本身是無法預測的
總結: (1)編譯器只會去看兩次從i讀資料的程式碼之間的程式碼沒有對i進行過操作,但是在內部對資料處理過程是很複雜,volatile會改變程式碼生成的彙編程式碼,就是優化彙編程式碼,和不優化的彙編程式碼。如果i是一個暫存器變數或者表示一個埠資料,他可能被內部執行緒排程機制,突然使用了這個暫存器把這個暫存器改變了,有些彙編語句改變記憶體的值時,編譯器並不知道,編譯器看你的程式沒有改變值,預設優化了,所以加了volatile就不會優化了
(2)在vc6中,一般除錯模式沒有進行程式碼優化,所以這個關鍵字的作用看不出來 ,在嵌入式中不一樣,
示例程式碼:
#define rREG (*(volatile unsigned int *)0x0FEF0E00)
解引用 強制型別轉換 實際暫存器地址
rREG = 0xffff0000 對暫存器直接賦值,rREG是解引用後的變數