閱讀499 返回首頁    go 阿裏雲 go 技術社區[雲棲]


自旋鎖

一、為什麼用自旋鎖
    操作係統鎖機製的基本原理,就是在某個鎖操作過程中不能與其他鎖操作交織執行,以免多個執行路徑對內核中某些重要的數據及數據結構進行同時操作而造成混亂。在不同的係統環境中,根據係統特點和操作需要,鎖機製可以用多種方式來實現。以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係統中,使用自旋鎖的意義不大,還容易因為遞歸調用自旋鎖造成死鎖。

最後更新:2017-04-02 06:51:26

  上一篇:go 海盜分金幣
  下一篇:go 關於MAP文件的使用