訊號量和自旋鎖的差別 .
1)核心同步措施
為了避免併發,防止競爭。核心提供了一組同步方法來提供對共享資料的保護。我們的重點不是介紹這些方法的詳細用法,而是強調為什麼使用這些方法和它們之間的差別。
Linux 使用的同步機制可以說從2.0到2.6以來不斷髮展完善。從最初的原子操作,到後來的訊號量,從大核心鎖到今天的自旋鎖。這些同步機制的發展伴隨Linux從單處理器到對稱多處理器的過度;伴隨著從非搶佔核心到搶佔核心的過度。鎖機制越來越有效,也越來越複雜。
目前來說核心中原子操作多用來做計數使用,其它情況最常用的是兩種鎖以及它們的變種:一個是自旋鎖,另一個是訊號量。我們下面就來著重介紹一下這兩種鎖機制。
(2)自旋鎖
自旋鎖是專為防止多處理器併發而引入的一種鎖,它在核心中大量應用於中斷處理等部分(對於單處理器來說,防止中斷處理中的併發可簡單採用關閉中斷的方式,不需要自旋鎖)。自旋鎖最多隻能被一個核心任務持有,如果一個核心任務試圖請求一個已被佔用(已經被持有)的自旋鎖,那麼這個任務就會一直進行忙迴圈——旋轉——等待鎖重新可用。要是鎖未被佔用,請求它的核心任務便能立刻得到它並且繼續進行。自旋鎖可以在任何時刻防止多於一個的核心任務同時進入臨界區,因此這種鎖可有效地避免多處理器上併發執行的核心任務競爭共享資源。
事實上,自旋鎖的初衷就是:在短期間內進行輕量級的鎖定。一個被佔用的自旋鎖使得請求它的執行緒在等待鎖重新可用的期間進行自旋(特別浪費處理器時間),所以自旋鎖不應該被持有時間過長。如果需要長時間鎖定的話, 最好使用訊號量。自旋鎖的基本形式如下:
spin_lock(&mr_lock);
//臨界區
spin_unlock(&mr_lock);
因為自旋鎖在同一時刻只能被最多一個核心任務持有,所以一個時刻只有一個執行緒允許存在於臨界區中。這點很好地滿足了對稱多處理機器需要的鎖定服務。在單處理器上,自旋鎖僅僅當作一個設定核心搶佔的開關。如果核心搶佔也不存在,那麼自旋鎖會在編譯時被完全剔除出核心。
簡單的說,自旋鎖在核心中主要用來防止多處理器中併發訪問臨界區,防止核心搶佔造成的競爭。另外自旋鎖不允許任務睡眠。關於死鎖:假設有一個或多個核心任務和一個或多個資源,每個核心都在等待其中的一個資源,但所有的資源都已經被佔用了。這便會發生所有核心任務都在相互等待,但它們永遠不會釋放已經佔有的資源,於是任何核心任務都無法獲得所需要的資源,無法繼續執行,這便意味著死鎖發生了。自死瑣是說自己佔有了某個資源,然後自己又申請自己已佔有的資源,顯然不可能再獲得該資源,因此就自縛手腳了。
(3)訊號量
Linux中的訊號量是一種睡眠鎖。如果有一個任務試圖獲得一個已被持有的訊號量時,訊號量會將其推入等待佇列,然後讓其睡眠。這時處理器獲得自由去執行其它程式碼。當持有訊號量的程序將訊號量釋放後,在等待佇列中的一個任務將被喚醒,從而便可以獲得這個訊號量。
訊號量的睡眠特性,使得訊號量適用於鎖會被長時間持有的情況;只能在程序上下文中使用,因為中斷上下文中是不能被排程的;另外當代碼持有訊號量時,不可以再持有自旋鎖。訊號量基本使用形式為:
static DECLARE_MUTEX(mr_sem);//宣告互斥訊號量
if(down_interruptible(&mr_sem))
//可被中斷的睡眠,當訊號來到,睡眠的任務被喚醒
//臨界區
up(&mr_sem);
(4)訊號量和自旋鎖區別
雖然聽起來兩者之間的使用條件複雜,其實在實際使用中訊號量和自旋鎖並不易混淆。注意以下原則:如果程式碼需要睡眠——這往往是發生在和使用者空間同步時——使用訊號量是唯一的選擇。如果需要在自旋鎖和訊號量中作選擇,應該取決於鎖被持有的時間長短。理想情況是所有的鎖都應該儘可能短的被持有,但是如果鎖的持有時間較長的話,使用訊號量是更好的選擇。另外,訊號量不同於自旋鎖,它不會關閉核心搶佔,所以持有訊號量的程式碼可以被搶佔。這意味者訊號量不會對影響排程反應時間帶來負面影響。
自旋鎖對訊號量的比較:
需求場合 建議的加鎖方法
低開銷加鎖 優先使用自旋鎖
短期鎖定 優先使用自旋鎖
長期加鎖 優先使用訊號量
中斷上下文中加鎖 使用自旋鎖
持有鎖是需要睡眠、排程 使用訊號量