更新時(shí)間:2022-09-14 09:45:40 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽1796次
Linux 內(nèi)核中最常見(jiàn)的鎖是自旋鎖。自旋鎖是一種最多可以由一個(gè)執(zhí)行線程持有的鎖。如果執(zhí)行線程在競(jìng)爭(zhēng)(已持有)時(shí)嘗試獲取自旋鎖,則線程繁忙循環(huán)自旋等待鎖變得可用。如果鎖沒(méi)有被爭(zhēng)用,線程可以立即獲取鎖并繼續(xù)。旋轉(zhuǎn)可防止多個(gè)執(zhí)行線程在任何時(shí)候進(jìn)入臨界區(qū)。請(qǐng)注意,可以在多個(gè)位置使用同一個(gè)鎖,因此可以保護(hù)和同步對(duì)給定數(shù)據(jù)結(jié)構(gòu)的所有訪問(wèn)。
回到上一章的門(mén)和鑰匙類比,自旋鎖類似于坐在門(mén)外,等待里面的人出來(lái)遞給你鑰匙。如果你走到門(mén)口,里面沒(méi)有人,你可以拿鑰匙進(jìn)入房間。如果您到達(dá)門(mén)口并且有人正在里面,您必須在外面等待鑰匙,有效地反復(fù)檢查它的存在。房間空了,你可以拿鑰匙進(jìn)去。多虧了鑰匙(讀:自旋鎖),房間內(nèi)(讀:臨界區(qū))同時(shí)只允許一個(gè)人(讀:執(zhí)行線程)進(jìn)入。
競(jìng)爭(zhēng)自旋鎖導(dǎo)致線程在等待鎖變?yōu)榭捎脮r(shí)自旋(實(shí)質(zhì)上是浪費(fèi)處理器時(shí)間)這一事實(shí)很重要。這種行為是自旋鎖的關(guān)鍵。它不是明智的做法是長(zhǎng)時(shí)間持有自旋鎖。這就是自旋鎖的本質(zhì):一個(gè)輕量級(jí)的單持有人鎖,應(yīng)該在短時(shí)間內(nèi)持有。爭(zhēng)用鎖時(shí)的另一種行為是將當(dāng)前線程置于睡眠狀態(tài),并在它可用時(shí)將其喚醒。然后處理器可以關(guān)閉并執(zhí)行其他代碼。這會(huì)產(chǎn)生一些開(kāi)銷(xiāo),最明顯的是切換出和切換回阻塞線程所需的兩個(gè)上下文切換,這肯定比用于實(shí)現(xiàn)自旋鎖的幾行代碼要多得多。因此,保持自旋鎖的時(shí)間少于兩次上下文切換的持續(xù)時(shí)間是明智的。因?yàn)槲覀兇蠖鄶?shù)人都有比測(cè)量上下文切換更好的事情要做,所以盡量縮短鎖定時(shí)間。
自旋鎖依賴于體系結(jié)構(gòu)并在匯編中實(shí)現(xiàn)。與架構(gòu)相關(guān)的代碼在<asm/spinlock.h>中定義。實(shí)際可用的接口在<linux/spinlock.h>中定義。自旋鎖的基本用途是
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
自旋鎖(&mr_lock);
/* 臨界區(qū) */
自旋解鎖(&mr_lock);
鎖最多只能由一個(gè)執(zhí)行線程同時(shí)持有。因此,一次只允許一個(gè)線程進(jìn)入臨界區(qū)。這為多處理機(jī)器上的并發(fā)提供了所需的保護(hù)。在單處理器機(jī)器上,鎖編譯掉并且不存在。它們只是充當(dāng)禁用和啟用內(nèi)核搶占的標(biāo)記。如果內(nèi)核搶占關(guān)閉,鎖會(huì)完全編譯掉。
自旋鎖可用于中斷處理程序,而信號(hào)量不能使用,因?yàn)樗鼈兲幱谛菝郀顟B(tài)。如果在中斷處理程序中使用了鎖,您還必須在獲得鎖之前禁用本地中斷(當(dāng)前處理器上的中斷請(qǐng)求)。否則,中斷處理程序可能會(huì)在持有鎖時(shí)中斷內(nèi)核代碼并嘗試重新獲取鎖。中斷處理程序旋轉(zhuǎn),等待鎖變得可用。然而,鎖持有者在中斷處理程序完成之前不會(huì)運(yùn)行。這是上一章討論的雙重獲取死鎖的一個(gè)例子。請(qǐng)注意,您只需要在當(dāng)前處理器。如果一個(gè)中斷發(fā)生在不同的處理器上,并且它在同一個(gè)鎖上旋轉(zhuǎn),它不會(huì)阻止鎖持有者(它在不同的處理器上)最終釋放鎖。
內(nèi)核提供了一個(gè)方便地禁用中斷和獲取鎖的接口。用法是
spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
無(wú)符號(hào)長(zhǎng)標(biāo)志;
spin_lock_irqsave(&mr_lock, flags);
/* 臨界區(qū) ... */
spin_unlock_irqrestore(&mr_lock, flags);
例程spin_lock_irqsave()保存中斷的當(dāng)前狀態(tài),在本地禁用它們,然后獲得給定的鎖。相反,spin_unlock_irqrestore()解鎖給定的鎖并將中斷返回到它們之前的狀態(tài)。這樣,如果中斷最初被禁用,您的代碼不會(huì)錯(cuò)誤地啟用它們,而是保持禁用它們。請(qǐng)注意,flags變量似乎是按值傳遞的。這是因?yàn)殒i定例程部分實(shí)現(xiàn)為宏。
在單處理器系統(tǒng)上,前面的示例仍然必須禁用中斷以防止中斷處理程序訪問(wèn)共享數(shù)據(jù),但鎖定機(jī)制已被編譯掉。鎖定和解鎖也分別禁用和啟用內(nèi)核搶占。如果大家想了解更多相關(guān)知識(shí),不妨來(lái)關(guān)注一下動(dòng)力節(jié)點(diǎn)的Linux教程,里面還有更豐富的知識(shí)等著大家去學(xué)習(xí),相信對(duì)大家一定會(huì)有所幫助的。
0基礎(chǔ) 0學(xué)費(fèi) 15天面授
有基礎(chǔ) 直達(dá)就業(yè)
業(yè)余時(shí)間 高薪轉(zhuǎn)行
工作1~3年,加薪神器
工作3~5年,晉升架構(gòu)
提交申請(qǐng)后,顧問(wèn)老師會(huì)電話與您溝通安排學(xué)習(xí)
初級(jí) 202925
初級(jí) 203221
初級(jí) 202629
初級(jí) 203743