歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> Linux文化

Linux系統內核的同步機制-自旋鎖


自旋鎖最多只能被一個可執行線程持有。自旋鎖不會引起調用者睡眠,如果一個執行線程試圖獲得一個已經被持有的自旋鎖,那麼線程就會一直進行忙循環,一直等待下去,在那裡看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。

由於自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高於互斥鎖。

信號量和讀寫信號量適合於保持時間較長的情況,它們會導致調用者睡眠,因此只能在進程上下文使用(_trylock的變種能夠在中斷上下文使用);而自旋鎖適合於保持時間非常短的情況,因為一個被爭用的自旋鎖使得請求它的線程在等待重新可用時自旋,特別浪費處理時間,這是自旋鎖的要害之處,所以自旋鎖不應該被長時間持有。在實際應用中自旋鎖代碼只有幾行,而持有自旋鎖的時間也一般不會超過兩次上下方切換,因線程一旦要進行切換,就至少花費切出切入兩次,自旋鎖的占用時間如果遠遠長於兩次上下文切換,我們就可以讓線程睡眠,這就失去了設計自旋鎖的意義。

如果被保護的共享資源只在進程上下文訪問,使用信號量保護該共享資源非常合適,如果對共享資源的訪問時間非常短,自旋鎖也可以。但是如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。

自旋鎖保持期間是搶占失效的,而信號量和讀寫信號量保持期間是可以被搶占的。自旋鎖只有在內核可搶占或SMP的情況下才真正需要,在單CPU且不可搶占的內核下,自旋鎖的所有操作都是空操作。

一個執行單元要想訪問被自旋鎖保護的共享資源,必須先得到鎖,在訪問完共享資源後,必須釋放鎖。如果在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將立即得到鎖;如果在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操作將自旋在那裡,直到該自旋鎖的保持者釋放了鎖。

無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。自旋鎖的實現和體系結構密切相關,代碼一般通過匯編實現,定義在文件,實際用到的接口定義在文件夾 中, 自旋鎖的API有:

CODE:spin_lock_init(x)

該宏用於初始化自旋鎖x。自旋鎖在真正使用前必須先初始化。該宏用於動態初始化指定的。

CODE:DEFINE_SPINLOCK(x)

該宏聲明一個自旋鎖x並初始化它。該宏在2.6.11中第一次被定義,在先前的內核中並沒有該宏。

CODE:SPIN_LOCK_UNLOCKED

該宏用於靜態初始化一個自旋鎖。

CODE:DEFINE_SPINLOCK(x)等同於spinlock_t x = SPIN_LOCK_UNLOCKEDspin_is_locked(x) )

該宏用於判斷自旋鎖x是否已經被某執行單元保持(即被鎖),如果是,返回真,否則返回假。

CODE:spin_unlock_wait(x)

該宏用於等待自旋鎖x變得沒有被任何執行單元保持,如果沒有任何執行單元保持該自旋鎖,該宏立即返回,否則將循環在那裡,直到該自旋鎖被保持者釋放。

CODE:spin_trylock(lock)

該宏盡力獲得自旋鎖lock,如果能立即獲得鎖,它獲得鎖並返回真,否則不能立即獲得鎖,立即返回假。它不會自旋等待lock被釋放。

CODE:spin_lock(lock)

該宏用於獲得自旋鎖lock,如果能夠立即獲得鎖,它就馬上返回,否則,它將自旋在那裡,直到該自旋鎖的保持者釋放,這時,它獲得鎖並返回。總之,只有它獲得鎖才返回。

CODE:spin_lock_irqsave(lock, flags)

該宏獲得自旋鎖的同時把標志寄存器的值保存到變量flags中並失效本地中斷。

CODE:spin_lock_irq(lock) 該宏類似於spin_lock_irqsave,只是該宏不保存標志寄存器的值。禁止本地中斷並獲取指定的鎖

CODE:spin_lock_bh(lock)

該宏在得到自旋鎖的同時失效本地軟中斷。

CODE:spin_unlock(lock)

該宏釋放自旋鎖lock,它與spin_trylock或spin_lock配對使用。如果spin_trylock返回假,表明沒有獲得自旋鎖,因此不必使用spin_unlock釋放。

CODE:spin_unlock_irqrestore(lock, flags)

該宏釋放自旋鎖lock的同時,也恢復標志寄存器的值為變量flags保存的值。它與spin_lock_irqsave配對使用。

CODE:spin_unlock_irq(lock)

該宏釋放自旋鎖lock的同時,並激活本地中斷。它與spin_lock_irq配對應用。

CODE:spin_unlock_bh(lock)

該宏釋放自旋鎖lock的同時,也使能本地的軟中斷。它與spin_lock_bh配對使用。

CODE:spin_trylock_irqsave(lock, flags)

該宏如果獲得自旋鎖lock,它也將保存標志寄存器的值到變量flags中,並且失效本地中斷,如果沒有獲得鎖,它什麼也不做。

因此如果能夠立即獲得鎖,它等同於spin_lock_irqsave,如果不能獲得鎖,它等同於spin_trylock。如果該宏獲得自旋鎖lock,那需要使用spin_unlock_irqrestore來釋放。

CODE:spin_trylock_irq(lock)

該宏類似於spin_trylock_irqsave,只是該宏不保存標志寄存器。如果該宏獲得自旋鎖lock,需要使用spin_unlock_irq來釋放。

CODE:spin_trylock_bh(lock) 該宏如果獲得了自旋鎖,它也將失效本地軟中斷。如果得不到鎖,它什麼也不做。因此,如果得到了鎖,它等同於spin_lock_bh,如果得不到鎖,它等同於spin_trylock。如果該宏得到了自旋鎖,需要使用spin_unlock_bh來釋放。

CODE:spin_can_lock(lock)

該宏用於判斷自旋鎖lock是否能夠被鎖,它實際是spin_is_locked取反。如果lock沒有被鎖,它返回真,否則,返回假。該宏在2.6.11中第一次被定義,在先前的內核中並沒有該宏。

自旋鎖的基本使用如下:

CODE:spinlock_t myr_lock = SPIN_LOCK_UNLOCKED;

spin_lock(&myr_lock);

/*臨界區*/

spin_unlock(&myr_lock);

因為自旋鎖在同一時刻至多被一個執行線程持有,所以一個時刻只能有一個線程位於臨界區,這就為多處理器提供了防止並發訪問所需的保護機制,但是在單處理器上,編譯的時候不會加入自旋鎖。它僅僅被當作一個設置內核搶占機制是否被啟用的開關。注意,Linux內核實現的自旋鎖是不可遞歸的,這一點不同於自旋鎖在其他操作系統中的實現,如果你想得到一個你正持有的鎖,你必須自旋,等待你自己釋放這個鎖,但是你處於自旋忙等待中,所以永遠沒有機會釋放鎖,於是你就被自己鎖死了,一定要注意!

自旋鎖可以用在中斷處理程序中,但是在使用時一定要在獲取鎖之前,首先禁止本地中斷(當前處理器上的中斷),否則中斷處理程序就可能打斷正持有鎖的內核代碼,有可能會試圖支爭用這個已經被持有的自旋鎖。這樣一來,中斷處理程序就會自旋,等待該鎖重新可用,但是鎖的持有者在這個中斷處理程序執行完畢之前不可能運行,這就會造成雙重請求死鎖。

自旋鎖與下半部:由於下半部(中斷程序下半部)可以搶占進程上下文中的代碼,所以當下半部和進程上下文共享數據時,必須對進程上下文中的共享數據進行保護,所以需要加鎖的同時還要禁止下半部執行。同樣,由於中斷處理程序可以搶占下半部,所以如果中斷處理程序和下半部共享數據,那麼就必須在獲取恰當的鎖的同時還要禁止中斷。對於軟中斷,無論是否同種類型,如果數據被軟中斷共享,那麼它必須得到鎖的保護,因為同種類型的兩個軟中斷也可以同進運行在一個系統的多個處理器上。但是,同一個處理器上的一個軟中斷絕不會搶占另一個軟中斷,因此,根本不需要禁止下半部。


Copyright © Linux教程網 All Rights Reserved