162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci.. _tw_volatile_considered_harmful: 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci.. include:: ../disclaimer-zh_TW.rst 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci:Original: :ref:`Documentation/process/volatile-considered-harmful.rst 862306a36Sopenharmony_ci <volatile_considered_harmful>` 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci如果想評論或更新本文的內容,請直接聯繫原文檔的維護者。如果你使用英文 1162306a36Sopenharmony_ci交流有困難的話,也可以向中文版維護者求助。如果本翻譯更新不及時或者翻 1262306a36Sopenharmony_ci譯存在問題,請聯繫中文版維護者:: 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 英文版維護者: Jonathan Corbet <corbet@lwn.net> 1562306a36Sopenharmony_ci 中文版維護者: 伍鵬 Bryan Wu <bryan.wu@analog.com> 1662306a36Sopenharmony_ci 中文版翻譯者: 伍鵬 Bryan Wu <bryan.wu@analog.com> 1762306a36Sopenharmony_ci 中文版校譯者: 張漢輝 Eugene Teo <eugeneteo@kernel.sg> 1862306a36Sopenharmony_ci 楊瑞 Dave Young <hidave.darkstar@gmail.com> 1962306a36Sopenharmony_ci 時奎亮 Alex Shi <alex.shi@linux.alibaba.com> 2062306a36Sopenharmony_ci 胡皓文 Hu Haowen <src.res.211@gmail.com> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci爲什麼不應該使用「volatile」類型 2362306a36Sopenharmony_ci================================ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciC程式設計師通常認爲volatile表示某個變量可以在當前執行的線程之外被改變;因此,在內核 2662306a36Sopenharmony_ci中用到共享數據結構時,常常會有C程式設計師喜歡使用volatile這類變量。換句話說,他們經 2762306a36Sopenharmony_ci常會把volatile類型看成某種簡易的原子變量,當然它們不是。在內核中使用volatile幾 2862306a36Sopenharmony_ci乎總是錯誤的;本文檔將解釋爲什麼這樣。 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci理解volatile的關鍵是知道它的目的是用來消除優化,實際上很少有人真正需要這樣的應 3162306a36Sopenharmony_ci用。在內核中,程式設計師必須防止意外的並發訪問破壞共享的數據結構,這其實是一個完全 3262306a36Sopenharmony_ci不同的任務。用來防止意外並發訪問的保護措施,可以更加高效的避免大多數優化相關的 3362306a36Sopenharmony_ci問題。 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci像volatile一樣,內核提供了很多原語來保證並發訪問時的數據安全(自旋鎖, 互斥量,內 3662306a36Sopenharmony_ci存屏障等等),同樣可以防止意外的優化。如果可以正確使用這些內核原語,那麼就沒有 3762306a36Sopenharmony_ci必要再使用volatile。如果仍然必須使用volatile,那麼幾乎可以肯定在代碼的某處有一 3862306a36Sopenharmony_ci個bug。在正確設計的內核代碼中,volatile能帶來的僅僅是使事情變慢。 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci思考一下這段典型的內核代碼:: 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci spin_lock(&the_lock); 4362306a36Sopenharmony_ci do_something_on(&shared_data); 4462306a36Sopenharmony_ci do_something_else_with(&shared_data); 4562306a36Sopenharmony_ci spin_unlock(&the_lock); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci如果所有的代碼都遵循加鎖規則,當持有the_lock的時候,不可能意外的改變shared_data的 4862306a36Sopenharmony_ci值。任何可能訪問該數據的其他代碼都會在這個鎖上等待。自旋鎖原語跟內存屏障一樣—— 它 4962306a36Sopenharmony_ci們顯式的用來書寫成這樣 —— 意味著數據訪問不會跨越它們而被優化。所以本來編譯器認爲 5062306a36Sopenharmony_ci它知道在shared_data裡面將有什麼,但是因爲spin_lock()調用跟內存屏障一樣,會強制編 5162306a36Sopenharmony_ci譯器忘記它所知道的一切。那麼在訪問這些數據時不會有優化的問題。 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci如果shared_data被聲名爲volatile,鎖操作將仍然是必須的。就算我們知道沒有其他人正在 5462306a36Sopenharmony_ci使用它,編譯器也將被阻止優化對臨界區內shared_data的訪問。在鎖有效的同時, 5562306a36Sopenharmony_cishared_data不是volatile的。在處理共享數據的時候,適當的鎖操作可以不再需要 5662306a36Sopenharmony_civolatile —— 並且是有潛在危害的。 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civolatile的存儲類型最初是爲那些內存映射的I/O寄存器而定義。在內核里,寄存器訪問也應 5962306a36Sopenharmony_ci該被鎖保護,但是人們也不希望編譯器「優化」臨界區內的寄存器訪問。內核里I/O的內存訪問 6062306a36Sopenharmony_ci是通過訪問函數完成的;不贊成通過指針對I/O內存的直接訪問,並且不是在所有體系架構上 6162306a36Sopenharmony_ci都能工作。那些訪問函數正是爲了防止意外優化而寫的,因此,再說一次,volatile類型不 6262306a36Sopenharmony_ci是必需的。 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci另一種引起用戶可能使用volatile的情況是當處理器正忙著等待一個變量的值。正確執行一 6562306a36Sopenharmony_ci個忙等待的方法是:: 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci while (my_variable != what_i_want) 6862306a36Sopenharmony_ci cpu_relax(); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cicpu_relax()調用會降低CPU的能量消耗或者讓位於超線程雙處理器;它也作爲內存屏障一樣出 7162306a36Sopenharmony_ci現,所以,再一次,volatile不是必需的。當然,忙等待一開始就是一種反常規的做法。 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci在內核中,一些稀少的情況下volatile仍然是有意義的: 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci - 在一些體系架構的系統上,允許直接的I/0內存訪問,那麼前面提到的訪問函數可以使用 7662306a36Sopenharmony_ci volatile。基本上,每一個訪問函數調用它自己都是一個小的臨界區域並且保證了按照 7762306a36Sopenharmony_ci 程式設計師期望的那樣發生訪問操作。 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci - 某些會改變內存的內聯彙編代碼雖然沒有什麼其他明顯的附作用,但是有被GCC刪除的可 8062306a36Sopenharmony_ci 能性。在彙編聲明中加上volatile關鍵字可以防止這種刪除操作。 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci - Jiffies變量是一種特殊情況,雖然每次引用它的時候都可以有不同的值,但讀jiffies 8362306a36Sopenharmony_ci 變量時不需要任何特殊的加鎖保護。所以jiffies變量可以使用volatile,但是不贊成 8462306a36Sopenharmony_ci 其他跟jiffies相同類型變量使用volatile。Jiffies被認爲是一種「愚蠢的遺留物" 8562306a36Sopenharmony_ci (Linus的話)因爲解決這個問題比保持現狀要麻煩的多。 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci - 由於某些I/0設備可能會修改連續一致的內存,所以有時,指向連續一致內存的數據結構 8862306a36Sopenharmony_ci 的指針需要正確的使用volatile。網絡適配器使用的環狀緩存區正是這類情形的一個例 8962306a36Sopenharmony_ci 子,其中適配器用改變指針來表示哪些描述符已經處理過了。 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci對於大多代碼,上述幾種可以使用volatile的情況都不適用。所以,使用volatile是一種 9262306a36Sopenharmony_cibug並且需要對這樣的代碼額外仔細檢查。那些試圖使用volatile的開發人員需要退一步想想 9362306a36Sopenharmony_ci他們真正想實現的是什麼。 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci非常歡迎刪除volatile變量的補丁 - 只要證明這些補丁完整的考慮了並發問題。 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci注釋 9862306a36Sopenharmony_ci---- 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci[1] https://lwn.net/Articles/233481/ 10162306a36Sopenharmony_ci[2] https://lwn.net/Articles/233482/ 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci致謝 10462306a36Sopenharmony_ci---- 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci最初由Randy Dunlap推動並作初步研究 10762306a36Sopenharmony_ci由Jonathan Corbet撰寫 10862306a36Sopenharmony_ci參考Satyam Sharma,Johannes Stezenbach,Jesper Juhl,Heikki Orsila, 10962306a36Sopenharmony_ciH. Peter Anvin,Philipp Hahn和Stefan Richter的意見改善了本檔。 11062306a36Sopenharmony_ci 111