1. 程式人生 > >搶佔式核心與非搶佔式核心中的自旋鎖(spinlock)的區別

搶佔式核心與非搶佔式核心中的自旋鎖(spinlock)的區別

一、概括

(1)自旋鎖適用於SMP系統,UP系統用spinlock是作死。

(2)保護模式下禁止核心搶佔的方法:1、執行終端服務例程時2、執行軟中斷和tasklet時3、設定本地CPU計數器preempt_count

(3)自旋鎖的忙等待的實際意義是:嘗試獲取自旋鎖的另一個程序不斷嘗試獲取被佔用的自旋鎖,中間只pause一下!

(4)在搶佔式核心的spin_lock巨集中,第一次關搶佔,目的是防止死鎖(防止一個已經獲取自旋鎖而未釋放的程序被搶佔!!)。而後又開搶佔,目的是讓已經釋放自旋鎖的程序可以被排程出去,讓其他程序可以進入臨界區。當然,開啟核心搶佔後,排程器排程的程序是不是在盲等的程序不可而知!

   

二、具有核心搶佔的spin_lock巨集

讓我們來詳細討論用於請求自旋鎖的spin_lock巨集。下面的描述都是針對支援SMP系統的搶佔式核心的。該巨集獲取自旋鎖的地址sip作為它的引數,並執行下面的操作:
呼叫preempt_disable()以禁用核心搶佔
呼叫函式__raw_spin_trylock(),它對自旋鎖的slock欄位執行原子性的測試和設定操作。該操作首先執行等價於下列彙編片段的一些指令:
movb$0,%a1

xchgb%al,slp->slock

組合語言指令xchg原子性地交換8位暫存器%al和slp->slock指示的記憶體單元的內容。隨後,如果存放在自旋鎖中的舊值是正數,函式就返回1,否則返回0。
如果自旋鎖中的舊值是正數,巨集結束;核心控制路徑已經獲得自旋鎖。
否則,核心控制路徑無法獲得自旋鎖,因此巨集必須執行迴圈一直到在其它CPU上執行的核心控制路徑釋放自旋鎖。呼叫preempt_enable()遞減在第1步遞增了的搶佔計數器。如果在執行spin_lock巨集之前核心搶佔被啟用,那麼其它程序此時可以取代等待自旋鎖的程序。
如果break_lock欄位等於0,則把它設定為1。通過檢測該欄位,擁有鎖並在其它CPU上執行的程序可以知道是否有其它程序在等待這個鎖。如果程序把持某個自旋鎖的時間太長,它可以提前釋放鎖以便等待相同自旋鎖的程序能夠繼續向前執行。
執行等待迴圈:
while(spin_is_locked(sip)&& slp->break_lock)
cpu_relax();
巨集cpu_relax()簡化為一條pause組合語言指令。在Pentium4模型中引入了這條指令以優化自旋鎖迴圈的執行。通過引入一個很短的延遲,加快了緊跟在鎖後面的程式碼的執行並減少了能源消耗。pause與早先的80x86微處理器模型是向後相容的,因為它對應rep;nop指令,也就是對應空操作。

跳轉回第1步,再次試圖獲取自旋鎖

三、非搶佔式核心中的spin_lock巨集
如果在核心編譯時沒有選擇核心搶佔選項,spin_lock巨集就與前面描述的spin_lock巨集有很大的區別。在這種情況下,巨集生成一個組合語言程式片段,它本質上等價於下面緊湊的忙等待:
1:lock;decbslp->lock
jns3f
2:pause
cmpb$0,slp->slock
jle2b
jmp1b
3;
組合語言指令decb遞減自旋鎖的值,該指令是原子的,因為它帶有lock位元組字首。隨後檢測符號標誌,如果它被清0,說明自旋鎖被設定為1,因此從標記3處繼續正常執行。否則,在標籤2處執行緊湊迴圈直到自旋鎖出現正值。然後從標籤1處開始重新執行,因為不檢查其它的處理器是否搶佔了鎖就繼續執行是不安全的。
spin_unlock巨集
spin_unlock巨集釋放以前獲得的自旋鎖,它本質上執行下列組合語言指令;
movb$1,slp->slock
並在隨後呼叫preempt_enable(),注意,因為現在的80x86微處理器總是原子地執行記憶體中的只寫訪問,所以不使用lock位元組。