LL和SC
多核處理器在執行并行訪問時, 很可能會出現多個核同時訪問共享資源的情況,操作系統通常采用鎖的機制來防止一個核在訪問共享資源時,該共享資源被其他核所改寫。當某個共享資源被鎖住時,只有獲得這個鎖的cpu核能夠操作共享資源,其他試圖訪問這個共享資源的cpu核只能等待當前持有鎖的cpu釋放掉鎖之后,才能獲取鎖去訪問共享資源。為保護共享資源不被打擾,需要建立一個臨界區域,臨界區的操作稱作原子操作。信號量是實現臨界區的一個方法,信號量是一個并發運行的進程共享的內存區域,用來保證某些資源一次只能被一個進程使用。
每個原子代碼段有下列結構:
wait(sem); /* 做原子操作 */ signal(sem);sem就是一個信號量,這個信號量有兩個值,1表示信號量在使用中,等待;0表示信號量空閑,可以使用。進行原子操作之前,wait()判斷sem的值,如果sem=1,等待sem=0。如果sem=0,把sem置1,然后執行原子操作。原子操作結束之后,signal()把sem清0。wait()函數的內部,也是這樣的一個原子操作。
mips使用ll(鏈接加載)和sc(條件存儲)兩條指令實現信號量機制。使用它們可以對一個變量實現任意的讀-修改-寫序列(ll讀,sc寫)。這種指令序列并不保證所有操作的原子性,但是只有當這些指令序列的執行確實沒有受到干擾,是原子操作的情況下才能成功完成。
ll : ll rt, offset(base) 從內存中加載一個word, 將處理器內部的一個不可見的硬件鏈接位置1,將加載指令的地址存儲在寄存器LLAddr中。
sc : sc rt, offset(base) 檢測上次ll指令執行后開始的讀-修改-寫序列是否真的沒有受到任何干擾(不可見的硬件鏈接位是1)。如果操作是原子性的,rt寄存器的值就會存儲到相應的內存地址,同時“真”值1返回到rt寄存器,設置不可見的硬件鏈接位為0。如果讀-修改-寫序列受到干擾(不可見的硬件鏈接位是0),操作不是原子性的,內存中的數據會保持不變,設置rt寄存器的值為0。
LLAddr : 寄存器,用于存儲最近運行的鏈接加載操作的物理地址,。
下面是wait(sem)的實現:
wait :/* 獲取信號量sem的地址,存到t0寄存器 */la t0, sem TrayAgain :/* 取sem的值,存到t1寄存器,設置鏈接位為1,把sem的值存到LLAddr寄存器 */ll t1, 0(t0)/* 如果信號量已經被占用,等待sem被釋放;如果sem=0,信號量空閑,可以執行下面操作 */bne t1, zero, WaitForSem/* t1=1 */li t1, 1/* 判斷鏈接位;如果是原子操作,把1保存到sem, t1=1,清鏈接位; 如果不是原子操作,不改變sem的值,t1=0 */sc t1, 0(t0)/* 如果t1 = 1,ll sc指令執行成功,獲取到信號量; 如果t1=0,獲取信號量失敗,重新嘗試獲取 */beq t1, zero, TryAgain/* 延遲槽 */nop/* wait函數返回,ll sc執行成功,進入臨界區,執行原子操作 */jr rall sc指令執行失敗的情況:
1. 在ll sc指令之間發生了異常,eret指令會破壞原子操作,清除不可見的硬件鏈接位
2. 多處理器系統的另一個cpu改寫了那個位置的內存數據,或改寫了附近的內存數據(在同一個cacheline)
作者聲明:本文是作者學習總結,能力有限,難免出現問題,如發現問題會及時修改。如有侵權,聯系本人刪除。
總結
- 上一篇: clonezilla使用_使用Clone
- 下一篇: Apache Flink fault t