自旋鎖(spinlock) 自旋鎖最多只能被一個可執(zhí)行線程持有。自旋鎖不會引起調(diào)用者睡眠,如果一個執(zhí)行線程試圖獲得一個已經(jīng)被持有的自旋鎖,那么線程就會一直進(jìn)行忙循環(huán),一直等待下去,在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名。 由于自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠(yuǎn)高于互斥鎖。 信號量和讀寫信號量適合于保持時間較長的情況,它們會導(dǎo)致調(diào)用者睡眠,因此只能在進(jìn)程上下文使用(_trylock的變種能夠在中斷上下文使用);而自旋鎖適合于保持時間非常短的情況,因為一個被爭用的自旋鎖使得請求它的線程在等待重新可用時自旋,特別浪費(fèi)處理時間,這是自旋鎖的要害之處,所以自旋鎖不應(yīng)該被長時間持有。在實(shí)際應(yīng)用中自旋鎖代碼只有幾行,而持有自旋鎖的時間也一般不會超過兩次上下方切換,因線程一旦要進(jìn)行切換,就至少花費(fèi)切出切入兩次,自旋鎖的占用時間如果遠(yuǎn)遠(yuǎn)長于兩次上下文切換,我們就可以讓線程睡眠,這就失去了設(shè)計自旋鎖的意義。 如果被保護(hù)的共享資源只在進(jìn)程上下文訪問,使用信號量保護(hù)該共享資源非常合適,如果對共享資源的訪問時間非常短,自旋鎖也可以。但是如果被保護(hù)的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。 自旋鎖保持期間是搶占失效的,而信號量和讀寫信號量保持期間是可以被搶占的。自旋鎖只有在內(nèi)核可搶占或SMP的情況下才真正需要,在單CPU且不可搶占的內(nèi)核下,自旋鎖的所有操作都是空操作。 一個執(zhí)行單元要想訪問被自旋鎖保護(hù)的共享資源,必須先得到鎖,在訪問完共享資源后,必須釋放鎖。如果在獲取自旋鎖時,沒有任何執(zhí)行單元保持該鎖,那么將立即得到鎖;如果在獲取自旋鎖時鎖已經(jīng)有保持者,那么獲取鎖操作將自旋在那里,直到該自旋鎖的保持者釋放了鎖。 無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執(zhí)行單元獲得鎖。自旋鎖的實(shí)現(xiàn)和體系結(jié)構(gòu)密切相關(guān),代碼一般通過匯編實(shí)現(xiàn),定義在文件<asm/spinlock.h>,實(shí)際用到的接口定義在文件夾<linux/spinlock.h> 中, 自旋鎖的API有: spin_lock_init(x) 該宏用于初始化自旋鎖x。自旋鎖在真正使用前必須先初始化。該宏用于動態(tài)初始化指定的。 DEFINE_SPINLOCK(x) 該宏聲明一個自旋鎖x并初始化它。該宏在2.6.11中第一次被定義,在先前的內(nèi)核中并沒有該宏。 SPIN_LOCK_UNLOCKED 該宏用于靜態(tài)初始化一個自旋鎖。 DEFINE_SPINLOCK(x)等同于spinlock_t x = SPIN_LOCK_UNLOCKEDspin_is_locked(x) 該宏用于判斷自旋鎖x是否已經(jīng)被某執(zhí)行單元保持(即被鎖),如果是,返回真,否則返回假。 spin_unlock_wait(x) 該宏用于等待自旋鎖x變得沒有被任何執(zhí)行單元保持,如果沒有任何執(zhí)行單元保持該自旋鎖,該宏立即返回,否則將循環(huán)在那里,直到該自旋鎖被保持者釋放。 spin_trylock(lock) 該宏盡力獲得自旋鎖lock,如果能立即獲得鎖,它獲得鎖并返回真,否則不能立即獲得鎖,立即返回假。它不會自旋等待lock被釋放。 spin_lock(lock) 該宏用于獲得自旋鎖lock,如果能夠立即獲得鎖,它就馬上返回,否則,它將自旋在那里,直到該自旋鎖的保持者釋放,這時,它獲得鎖并返回。總之,只有它獲得鎖才返回。 spin_lock_irqsave(lock, flags) 該宏獲得自旋鎖的同時把標(biāo)志寄存器的值保存到變量flags中并失效本地中斷。 spin_lock_irq(lock) 該宏類似于spin_lock_irqsave,只是該宏不保存標(biāo)志寄存器的值。禁止本地中斷并獲取指定的鎖 spin_lock_bh(lock) 該宏在得到自旋鎖的同時失效本地軟中斷。 spin_unlock(lock) 該宏釋放自旋鎖lock,它與spin_trylock或spin_lock配對使用。如果spin_trylock返回假,表明沒有獲得自旋鎖,因此不必使用spin_unlock釋放。 spin_unlock_irqrestore(lock, flags) 該宏釋放自旋鎖lock的同時,也恢復(fù)標(biāo)志寄存器的值為變量flags保存的值。它與spin_lock_irqsave配對使用。 spin_unlock_irq(lock) 該宏釋放自旋鎖lock的同時,并激活本地中斷。它與spin_lock_irq配對應(yīng)用。 spin_unlock_bh(lock) 該宏釋放自旋鎖lock的同時,也使能本地的軟中斷。它與spin_lock_bh配對使用。 spin_trylock_irqsave(lock, flags) 該宏如果獲得自旋鎖lock,它也將保存標(biāo)志寄存器的值到變量flags中,并且失效本地中斷,如果沒有獲得鎖,它什么也不做。 因此如果能夠立即獲得鎖,它等同于spin_lock_irqsave,如果不能獲得鎖,它等同于spin_trylock。如果該宏獲得自旋鎖lock,那需要使用spin_unlock_irqrestore來釋放。 spin_trylock_irq(lock) 該宏類似于spin_trylock_irqsave,只是該宏不保存標(biāo)志寄存器。如果該宏獲得自旋鎖lock,需要使用spin_unlock_irq來釋放。 spin_trylock_bh(lock) 該宏如果獲得了自旋鎖,它也將失效本地軟中斷。如果得不到鎖,它什么也不做。因此,如果得到了鎖,它等同于spin_lock_bh,如果得不到鎖,它等同于spin_trylock。如果該宏得到了自旋鎖,需要使用spin_unlock_bh來釋放。 spin_can_lock(lock) 該宏用于判斷自旋鎖lock是否能夠被鎖,它實(shí)際是spin_is_locked取反。如果lock沒有被鎖,它返回真,否則,返回假。該宏在2.6.11中第一次被定義,在先前的內(nèi)核中并沒有該宏。 自旋鎖的基本使用如下: spinlock_t myr_lock = SPIN_LOCK_UNLOCKED; spin_lock(&myr_lock); /*臨界區(qū)*/ spin_unlock(&myr_lock); 因為自旋鎖在同一時刻至多被一個執(zhí)行線程持有,所以一個時刻只能有一個線程位于臨界區(qū),這就為多處理器提供了防止并發(fā)訪問所需的保護(hù)機(jī)制,但是在單處理器上,編譯的時候不會加入自旋鎖。它僅僅被當(dāng)作一個設(shè)置內(nèi)核搶占機(jī)制是否被啟用的開關(guān)。注意,Linux內(nèi)核實(shí)現(xiàn)的自旋鎖是不可遞歸的,這一點(diǎn)不同于自旋鎖在其他操作系統(tǒng)中的實(shí)現(xiàn),如果你想得到一個你正持有的鎖,你必須自旋,等待你自己釋放這個鎖,但是你處于自旋忙等待中,所以永遠(yuǎn)沒有機(jī)會釋放鎖,于是你就被自己鎖死了,一定要注意! 自旋鎖可以用在中斷處理程序中,但是在使用時一定要在獲取鎖之前,首先禁止本地中斷(當(dāng)前處理器上的中斷),否則中斷處理程序就可能打斷正持有鎖的內(nèi)核代碼,有可能會試圖支爭用這個已經(jīng)被持有的自旋鎖。這樣一來,中斷處理程序就會自旋,等待該鎖重新可用,但是鎖的持有者在這個中斷處理程序執(zhí)行完畢之前不可能運(yùn)行,這就會造成雙重請求死鎖。 自旋鎖與下半部:由于下半部(中斷程序下半部)可以搶占進(jìn)程上下文中的代碼,所以當(dāng)下半部和進(jìn)程上下文共享數(shù)據(jù)時,必須對進(jìn)程上下文中的共享數(shù)據(jù)進(jìn)行保護(hù),所以需要加鎖的同時還要禁止下半部執(zhí)行。同樣,由于中斷處理程序可以搶占下半部,所以如果中斷處理程序和下半部共享數(shù)據(jù),那么就必須在獲取恰當(dāng)?shù)逆i的同時還要禁止中斷。對于軟中斷,無論是否同種類型,如果數(shù)據(jù)被軟中斷共享,那么它必須得到鎖的保護(hù),因為同種類型的兩個軟中斷也可以同進(jìn)運(yùn)行在一個系統(tǒng)的多個處理器上。但是,同一個處理器上的一個軟中斷絕不會搶占另一個軟中斷,因此,根本不需要禁止下半部。
|