宋寶華:關於ARM Linux原子操做的實現

本文系轉載,著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。linux

做者: 宋寶華微信

來源: 微信公衆號linux閱碼場(id: linuxdev)線程


競態無所不在

首先咱們要理解競態(race condition)無所不在,哪怕是對一個全局變量作++的加1動做。翻譯

a=0
a++;

a++這句話,會被翻譯爲多條指令:code

ldr     r3, [r3, #0] 
adds    r2, r3, #1
str     r2, [r3, #0] 

它會先讀(ldr),再修改(add),再寫(str),是一個典型的讀-修改-寫(RMW)序列。a++在硬件上不是原子的!orm

假設2個線程(或者1個線程1箇中斷)「同時」作a++,由於加了2次,理論上a應該是等於2,可是結果a可能只是等於1,緣由很簡單:blog

file

假設第2個線程,在第一個線程作完讀(LDR)以後,搶入率先作完a++,顯然這個時候a=1,可是因爲第一個線程在ldr指令裏面已經讀到了a=0,第1個線程在第2個線程作完a++後,繼續作++仍是會在0的基礎上面加(只須要執行add和str指令了),因此致使第1個線程再++後,a仍是等於1.it

解決這樣的race condition,咱們須要把2個線程的a++的讀-修改-寫序列,串行化,彼此排他化。io

也就是把這種交錯的RMW:form

file

變成這種前後發生的RMW:

file

這樣第2個序列能夠讀到1,而且在1的基礎上加1,保證結果是2。

LDREX和STREX

ARM V7以後的LDREX、STREX指令能夠解決這個問題。它保證2個讀-修改-寫序列有交叉的時候,只有1個能夠寫成功,另一個則再次嘗試。

好比下面這個序列,R用的LDREX,W用的STREX,則只有第一個線程的STREX能夠成功,而第二個的W(STREX)會失敗:

file

相似以下:

file

那麼,這個執行strex失敗的線程2,會把第一條的LDREX指令從新執行一次:

file

STREX指令,除了把寄存器的值寫入一個地址之外,還能夠返回此次寫是否成功。

STREXEQ r0, r1, [LockAddr]

上述指令把r1寫入地址LockAddr,若是寫入成功,則r0=0,不然r0不等於0。若是r0不等於0,證實寫入失敗,那麼須要從新來ldrex,從新來修改和寫。官方解釋以下:

The STREX instruction performs a conditionalstore of a word to memory. If the exclusive monitor(s) permit thestore, the operation updates the memory location and returns the value0 in the destination register, indicating that the operation succeeded.If the exclusive monitor(s) do not permit the store, the operationdoes not update the memory location and returns the value 1 in thedestination register. This makes it possible to implement conditionalexecution paths based on the success or failure of the memory operation.For example, STREX R2, R1, [R0] performs a Store-Exclusiveoperation to the address in R0, conditionallystoring the value from R1 and indicating successor failure in R2.

相似以下流程:

file

當兩個LDREX,STREX序列交錯的時候,誰先STREX,誰成功,第2個STREX失敗,相似:

file

因此誰先LDREX不是重點,重點是誰先STREX誰成功,後STREX的從新來LDREX。

更多精彩更新中……歡迎關注微信公衆號:linux閱碼場(id: linuxdev)

相關文章
相關標籤/搜索