找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1703|回復: 0
打印 上一主題 下一主題
收起左側(cè)

Linux內(nèi)核的同步機制之二--自旋鎖

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

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表