標(biāo)題: Linux內(nèi)核的同步機(jī)制之二--自旋鎖 [打印本頁]

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






歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1