一、為什麼用自旋鎖
作業系統鎖機制的基本原理,就是在某個鎖操作過程中不能與其他鎖操作交織執行,以免多個執行路徑對核心中某些重要的資料及資料結構進行同時操作而造成混亂。在不同的系統環境中,根據系統特點和操作需要,鎖機制可以用多種方式來實作。以Linux為例,其系統核心的鎖機制一般通過3 種基本方式來實作,即原語、關中斷和總線鎖。在單CPU系統中,CPU 的讀—修改—寫原語可以保證是原子的,即執行過程過中不會被中斷,是以CPU 通過關中斷的方式,從晶片級保證該操作所存取的資料不能被多個核心控制路徑同時通路,避免交叉執行。然而,在對稱多處理器 (SMP) 環境中,單CPU 涉及讀—修改—寫原語不再是原子的,因為,在某個CPU 執行讀—修改—寫指令時有多次總線操作,其他CPU 競争總線,可導緻對同一存儲單元的讀—寫操作與其他CPU 對這一存儲單元交叉,這時我們就需要用一個稱為自旋鎖(spin lock)的原始對象為CPU 提供鎖定總線的方法。
二、自旋鎖是什麼
自旋鎖(spin lock)是一個典型的對臨界資源的互斥手段,它的名稱來源于它的特性。為了獲得一個自旋鎖,在某CPU上運作的代碼需先執行一個原子操作,該操作測試并設定(test-and-set)某個記憶體變量,由于它是原子操作,是以在該操作完成之前其它CPU不可能通路這個記憶體變量。如果測試結果表明鎖已經空閑,則程式獲得這個自旋鎖并繼續執行。如果測試結果表明鎖仍被占用,程式将在一個小的循環内重複這個“測試并設定(test-and-set)”操作,即開始“自旋”。最後,鎖的所有者通過重置該變量釋放這個自旋鎖,于是,某個等待的test-and-set操作向其調用者報告鎖已釋放。
了解自旋鎖最簡單的方法是把它作為一個變量看待,這個變量把一個臨界區或者标記為“我目前在另一個CPU上運作,請稍等一會”,或者标記為“我目前不在運作,可以被使用”。如果1号CPU首先進入該例程,它就擷取該自旋鎖;當2号CPU試圖進入同一個例程時,該自旋鎖告訴它自己已為1号CPU所持有,需等到1号CPU釋放自己後才能進入。一個簡單的自旋鎖實作結構如下:
/ /定義一個自旋鎖
sp inlock_tmylock = SPIN_LOCK_UNLOCKED;
sp in_lock (&mylock) ; / /将臨界區鎖住
. . .
critical section / /臨界區
sp in_unlock (&mylock) ; / /解鎖
這是自旋鎖的一種常用方法,注意它沒有開關中斷,也沒有儲存狀态字,因為開關中斷對SMP系統來說,開銷是比較大的。
三、關于自旋鎖的幾個事實
自旋鎖實際上是忙等鎖,當鎖不可用時,CPU一直循環執行“測試并設定(test-and-set)”該鎖直到可用而取得該鎖,CPU在等待自旋鎖時不做任何有用的工作,僅僅是等待。這說明隻有在占用鎖的時間極短的情況下,使用自旋鎖是合理的,因為此時某個CPU可能正在等待這個自旋鎖。當臨界區較為短小時,如隻是為了保證對資料修改的原子性,常用自旋鎖;當臨界區很大,或有共享裝置的時候,需要較長時間占用鎖,使用自旋鎖就不是一個很好的選擇,會降低CPU的效率。
自旋鎖也存在死鎖(deadlock)問題。引發這個問題最常見的情況是要求遞歸使用一個自旋鎖,即如果一個已經擁有某個自旋鎖的CPU想第二次獲得這個自旋鎖,則該CPU将死鎖。自旋鎖沒有與其關聯的“使用計數器”或“所有者辨別”;鎖或者被占用或者空閑。如果你在鎖被占用時擷取它,你将等待到該鎖被釋放。如果碰巧你的CPU已經擁有了該鎖,那麼用于釋放鎖的代碼将得不到運作,因為你使CPU永遠處于“測試并設定”某個記憶體變量的自旋狀态。另外,如果程序獲得自旋鎖之後再阻塞,也有可能導緻死鎖的發生。由于自旋鎖造成的死鎖,會使整個系統挂起,影響非常大。
自旋鎖一定是由系統核心調用的。不可能在使用者程式中由使用者請求自旋鎖。當一個使用者程序擁有自旋鎖期間,核心是把代碼提升到管态的級别上運作。在内部,核心能擷取自旋鎖,但任何使用者都做不到這一點
四、自旋鎖與信号量比較
自旋鎖和信号量是解決互斥問題的基本手段,無論是單處理系統還是多處理系統,它們可以不需修改代碼地進行移植。那麼,這兩個手段應該如何選擇呢?這就要考慮臨界區的性質和系統處理的要求。
從嚴格意義上說,信号量和自旋鎖屬于不同層次的互斥手段,前者的實作有賴于後者。
信号量是程序級的,用于多個程序之間對資源的互斥,雖然也是在核心中,但是該核心執行路徑是以程序的身份,代表程序來争奪資源的。如果競争不上,會有上下文切換,程序可以去睡眠,但CPU不會停,會接着運作其他的執行路徑。從概念上說,這與單CPU或多CPU沒有直接的關系,隻是在信号量本身的實作上,為了保證信号量結構存取的原子性,在多CPU中需要自旋鎖來互斥。但是值得注意的是上下文切換需要一定時間,并且會使高速緩沖失效,對系統性能影響是很大的。是以,隻有當程序占用資源很長時間時,用信号量才是不錯的選擇。
當所要保護的臨界區比較短時,用自旋鎖是非常友善的,因為它節省上下文切換的時間。但是CPU得不到自旋鎖會在那裡空轉直到鎖成功為止,是以要求鎖不能在臨界區裡停留很長時間,否則會降低系統的效率。
綜上,自旋鎖是一種保護資料結構或代碼片段的原始方式,主要用于SMP中,用于CPU同步,在某個時刻隻允許一個程序通路臨界區内的代碼。它的實作是基于CPU鎖定資料總線的指令。為保證系統效率,自旋鎖鎖定的臨界區一般比較短。在單CPU系統中,使用自旋鎖的意義不大,還容易因為遞歸調用自旋鎖造成死鎖。