爲何不該該使用「volatile」類型

如下文檔來自於內核linux-2.6.32.6\Documentation\zh_CN\volatile-considered-harmful.txt

C程序員一般認爲volatile表示某個變量能夠在當前執行的線程以外被改變;所以,在內核
中用到共享數據結構時,經常會有C程序員喜歡使用volatile這類變量。換句話說,他們經
常會把volatile類型當作某種簡易的原子變量,固然它們不是。在內核中使用volatile幾
乎老是錯誤的;本文檔將解釋爲何這樣。

理解volatile的關鍵是知道它的目的是用來消除優化,實際上不多有人真正須要這樣的應
用。在內核中,程序員必須防止意外的併發訪問破壞共享的數據結構,這實際上是一個徹底
不一樣的任務。用來防止意外併發訪問的保護措施,能夠更加高效的避免大多數優化相關的
問題。

像volatile同樣,內核提供了不少原語來保證併發訪問時的數據安全(自旋鎖, 互斥量,內
存屏障等等),一樣能夠防止意外的優化。若是能夠正確使用這些內核原語,那麼就沒有
必要再使用volatile。若是仍然必須使用volatile,那麼幾乎能夠確定在代碼的某處有一
個bug。在正確設計的內核代碼中,volatile能帶來的僅僅是使事情變慢。

思考一下這段典型的內核代碼:

    spin_lock(&the_lock);
    do_something_on(&shared_data);
    do_something_else_with(&shared_data);
    spin_unlock(&the_lock);

若是全部的代碼都遵循加鎖規則,當持有the_lock的時候,不可能意外的改變shared_data的
值。任何可能訪問該數據的其餘代碼都會在這個鎖上等待。自旋鎖原語跟內存屏障同樣—— 它
們顯式的用來書寫成這樣 —— 意味着數據訪問不會跨越它們而被優化。因此原本編譯器認爲
它知道在shared_data裏面將有什麼,可是由於spin_lock()調用跟內存屏障同樣,會強制編
譯器忘記它所知道的一切。那麼在訪問這些數據時不會有優化的問題。

若是shared_data被聲名爲volatile,鎖操做將仍然是必須的。就算咱們知道沒有其餘人正在
使用它,編譯器也將被阻止優化對臨界區內shared_data的訪問。在鎖有效的同時,
shared_data不是volatile的。在處理共享數據的時候,適當的鎖操做能夠再也不須要
volatile —— 而且是有潛在危害的。

volatile的存儲類型最初是爲那些內存映射的I/O寄存器而定義。在內核裏,寄存器訪問也應
該被鎖保護,可是人們也不但願編譯器「優化」臨界區內的寄存器訪問。內核裏I/O的內存訪問
是經過訪問函數完成的;不同意經過指針對I/O內存的直接訪問,而且不是在全部體系架構上
都能工做。那些訪問函數正是爲了防止意外優化而寫的,所以,再說一次,volatile類型不
是必需的。

另外一種引發用戶可能使用volatile的狀況是當處理器正忙着等待一個變量的值。正確執行一
個忙等待的方法是:

    while (my_variable != what_i_want)
        cpu_relax();

cpu_relax()調用會下降CPU的能量消耗或者讓位於超線程雙處理器;它也做爲內存屏障同樣出
現,因此,再一次,volatile不是必需的。固然,忙等待一開始就是一種反常規的作法。

在內核中,一些稀少的狀況下volatile仍然是有意義的:

  - 在一些體系架構的系統上,容許直接的I/0內存訪問,那麼前面提到的訪問函數可使用
    volatile。基本上,每個訪問函數調用它本身都是一個小的臨界區域而且保證了按照
    程序員指望的那樣發生訪問操做。

  - 某些會改變內存的內聯彙編代碼雖然沒有什麼其餘明顯的附做用,可是有被GCC刪除的可
    能性。在彙編聲明中加上volatile關鍵字能夠防止這種刪除操做。

  - Jiffies變量是一種特殊狀況,雖然每次引用它的時候均可以有不一樣的值,但讀jiffies
    變量時不須要任何特殊的加鎖保護。因此jiffies變量可使用volatile,可是不同意
    其餘跟jiffies相同類型變量使用volatile。Jiffies被認爲是一種「愚蠢的遺留物"
    (Linus的話)由於解決這個問題比保持現狀要麻煩的多。

  - 因爲某些I/0設備可能會修改連續一致的內存,因此有時,指向連續一致內存的數據結構
    的指針須要正確的使用volatile。網絡適配器使用的環狀緩存區正是這類情形的一個例
    子,其中適配器用改變指針來表示哪些描述符已經處理過了。

對於大多代碼,上述幾種可使用volatile的狀況都不適用。因此,使用volatile是一種
bug而且須要對這樣的代碼額外仔細檢查。那些試圖使用volatile的開發人員須要退一步想一想
他們真正想實現的是什麼。
相關文章
相關標籤/搜索