Linux内核ARM构架中原子变量的底层实现研究
?前段時間重新研究了一下Linux的并發控制機制,對于內核的自旋鎖、互斥鎖、信號量等機制及其變體做了底層代碼上的研究。因為只有從原理上理解了這些機制,在編寫驅動的時候才會記得應該注意什么。這些機制基本都從代碼上理解了,但是唯有一個不是非常理解的是內核對于ARM構架中原子變量的底層支持,這個機制其實在自旋鎖、互斥鎖以及讀寫鎖等內核機制中都有類似的使用。這里將學習的結果寫出,請大家指正。
? ? 假設原子變量的底層實現是由一個匯編指令實現的,這個原子性必然有保障。但是如果原子變量的實現是由多條指令組合而成的,那么對于SMP和中斷的介入會不會有什么影響呢?我在看ARM的原子變量操作實現的時候,發現其是由多條匯編指令(ldrex/strex)實現的。在參考了別的書籍和資料后,發現大部分書中對這兩條指令的描訴都是說他們是支持在SMP系統中實現多核共享內存的互斥訪問。但在UP系統中使用,如果ldrex/strex和之間發生了中斷,并在中斷中也用ldrex/strex操作了同一個原子變量會不會有問題呢?就這個問題,我認真看了一下內核的ARM原子變量源碼和ARM官方對于ldrex/strex的功能解釋,總結如下:
?
一、ARM構架的原子變量實現結構
? ? 對于ARM構架的原子變量實現源碼位于:arch/arm/include/asm/atomic.h
? ? 其主要的實現代碼分為ARMv6以上(含v6)構架的實現和ARMv6版本以下的實現。
該文件的主要結構如下:
? ? ??這樣的安排是依據ARM核心指令集版本的實現來做的:
(1)在ARMv6以上(含v6)構架有了多核的CPU,為了在多核之間同步數據和控制并發,ARM在內存訪問上增加了獨占監測(Exclusive monitors)機制(一種簡單的狀態機),并增加了相關的ldrex/strex指令。請先閱讀以下參考資料(關鍵在于理解local monitor和Global monitor):
1.2.2.?Exclusive monitors
4.2.12.?LDREX?和?STREX
(2)對于ARMv6以前的構架不可能有多核CPU,所以對于變量的原子訪問只需要關閉本CPU中斷即可保證原子性。?
對于(2),非常好理解。
但是(1)情況,我還是要通過源碼的分析才認同這種代碼,以下我僅僅分析最具有代表性的atomic_add源碼,其他的API原理都一樣。如果讀者還不熟悉C內嵌匯編的格式,請參考《ARM GCC?內嵌匯編手冊》
?
二、內核對于ARM構架的atomic_add源碼分析
源碼分析:?
注意:根據內聯匯編的語法,result、tmp、&v->counter對應的數據都放在了寄存器中操作。如果出現上下文切換,切換機制會做寄存器上下文保護。
?(1)ldrex %0, [%3]
意思是將&v->counter指向的數據放入result中,并且(分別在Local monitor和Global monitor中)設置獨占標志。
(2)add %0, %0, %4
result = result + i
(3)strex %1, %0, [%3]
意思是將result保存到&v->counter指向的內存中,此時?Exclusive monitors會發揮作用,將保存是否成功的標志放入tmp中。
(4)?teq %1, #0
測試strex是否成功(tmp == 0???)
(5)bne 1b
如果發現strex失敗,從(1)再次執行。
? ? ??通過上面的分析,可知關鍵在于strex的操作是否成功的判斷上。而這個就歸功于ARM的Exclusive monitors和ldrex/strex指令的機制。以下通過可能的情況分析ldrex/strex指令機制。(請閱讀時參考4.2.12.?LDREX?和?STREX)
?
1、UP系統或SMP系統中變量為非CPU間共享訪問的情況?
? ? 此情況下,僅有一個CPU可能訪問變量,此時僅有Local monitor需要關注。
? ? 假設CPU執行到(2)的時候,來了一個中斷,并在中斷里使用ldrex/strex操作了同一個原子變量。則情況如下圖所示:
- A:處理器標記一個物理地址,但訪問尚未完畢
- B:再次標記此物理地址訪問尚未完畢(與A重復)
- C:進行存儲操作,清除以上標記,返回0(操作成功)
- D:不會進行存儲操作,并返回1(操作失敗)?
也就是說,中斷例程里的操作會成功,被中斷的操作會失敗重試。?
?
2、SMP系統中變量為CPU間共享訪問的情況
??
? ? 此情況下,需要兩個CPU間的互斥訪問,此時ldrex/strex指令會同時關注Local monitor和Global monitor。
(i)兩個CPU同時訪問同個原子變量(ldrex/strex指令會關注Global monitor。)
- A:將該物理地址標記為CPU0獨占訪問,并清除CPU0對其他任何物理地址的任何獨占訪問標記。
- B:標記此物理地址為CPU1獨占訪問,并清除CPU1對其他任何物理地址的任何獨占訪問標記。
- C:沒有標記為CPU0獨占訪問,不會進行存儲,并返回1(操作失敗)。
- D:已被標記為CPU1獨占訪問,進行存儲并清除獨占訪問標記,并返回0(操作成功)。
?也就是說,后執行ldrex操作的CPU會成功。
?
(ii)同一個CPU因為中斷,“嵌套”訪問同個原子變量(ldrex/strex指令會關注Local monito)
- A:將該物理地址標記為CPU0獨占訪問,并清除CPU0對其他任何物理地址的任何獨占訪問標記。
- B:再次標記此物理地址為CPU0獨占訪問,并清除CPU0對其他任何物理地址的任何獨占訪問標記。
- C:已被標記為CPU0獨占訪問,進行存儲并清除獨占訪問標記,并返回0(操作成功)。
- D:沒有標記為CPU0獨占訪問,不會進行存儲,并返回1(操作失敗)。
也就是說,中斷例程里的操作會成功,被中斷的操作會失敗重試。
?
(iii)兩個CPU同時訪問同個原子變量,并同時有CPU因中斷“嵌套”訪問改原子變量(ldrex/strex指令會同時關注Local monitor和Global monitor)
雖然對于人來說,這種情況比較BT。但是在飛速運行的CPU來說,BT的事情隨時都可能發生。
- A:將該物理地址標記為CPU0獨占訪問,并清除CPU0對其他任何物理地址的任何獨占訪問標記。
- B:標記此物理地址為CPU1獨占訪問,并清除CPU1對其他任何物理地址的任何獨占訪問標記。
- C:再次標記此物理地址為CPU0獨占訪問,并清除CPU0對其他任何物理地址的任何獨占訪問標記。
- D:已被標記為CPU0獨占訪問,進行存儲并清除獨占訪問標記,并返回0(操作成功)。
- E:沒有標記為CPU1獨占訪問,不會進行存儲,并返回1(操作失敗)。
- F:沒有標記為CPU0獨占訪問,不會進行存儲,并返回1(操作失敗)。
?
? ? 當然還有其他許多復雜的可能,也可以通過ldrex/strex指令的機制分析出來。從上面列舉的分析中,我們可以看出:ldrex/strex可以保證在任何情況下(包括被中斷)的訪問原子性。所以內核中ARM構架中的原子操作是可以信任的。
總結
以上是生活随笔為你收集整理的Linux内核ARM构架中原子变量的底层实现研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为你践行是什么意思
- 下一篇: 小班感恩节教案《亲亲一家人》反思