W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
多年使用鎖的經(jīng)驗(yàn) -- 早于 Linux 的經(jīng)驗(yàn) -- 已經(jīng)表明加鎖可能是非常難于正確的. 管理并發(fā)是一個(gè)固有的技巧性的事情, 有很多出錯(cuò)的方式. 在這一節(jié), 我們快速看一下可能出錯(cuò)的東西.
如同上面已經(jīng)說過的, 一個(gè)正確的加鎖機(jī)制需要清晰和明確的規(guī)則. 當(dāng)你創(chuàng)建一個(gè)可以被并發(fā)存取的資源時(shí), 你應(yīng)當(dāng)定義哪個(gè)鎖將控制存取. 加鎖應(yīng)當(dāng)真正在開始處進(jìn)行; 事后更改會(huì)是難的事情. 開始時(shí)花費(fèi)的時(shí)間常常在調(diào)試時(shí)獲得回報(bào).
當(dāng)你編寫你的代碼, 你會(huì)毫無疑問遇到幾個(gè)函數(shù)需要存取通過一個(gè)特定鎖保護(hù)的結(jié)構(gòu). 在此, 你必須小心: 如果一個(gè)函數(shù)需要一個(gè)鎖并且接著調(diào)用另一個(gè)函數(shù)也試圖請(qǐng)求這個(gè)鎖, 你的代碼死鎖. 不論旗標(biāo)還是自旋鎖都不允許一個(gè)持鎖者第 2 次請(qǐng)求鎖; 如果你試圖這樣做, 事情就簡單地完了.
為使的加鎖正確工作, 你不得不編寫一些函數(shù), 假定它們的調(diào)用者已經(jīng)獲取了相關(guān)的鎖. 常常地, 只有你的內(nèi)部的, 靜態(tài)函數(shù)能夠這樣編寫; 從外部調(diào)用的函數(shù)必須明確處理加鎖. 當(dāng)你編寫內(nèi)部函數(shù)對(duì)加鎖做了假設(shè), 方便自己(和其他使用你的代碼的人)并且明確記錄這些假設(shè). 在幾個(gè)月后可能很難回來并記起是否你需要持有一個(gè)鎖來調(diào)用一個(gè)特殊函數(shù).
在 sucll 的例子里, 采用的設(shè)計(jì)決定是要求所有的函數(shù)直接從系統(tǒng)調(diào)用里調(diào)用, 來請(qǐng)求應(yīng)用到被存取的設(shè)備結(jié)構(gòu)上的旗標(biāo). 所有的內(nèi)部函數(shù), 那些只是從其他 scull 函數(shù)里調(diào)用的, 可以因此假設(shè)旗標(biāo)已經(jīng)正確獲得.
在有大量鎖的系統(tǒng)中(并且內(nèi)核在成為這樣一個(gè)系統(tǒng)), 一次需要持有多于一個(gè)鎖, 對(duì)代碼是不尋常的. 如果某類計(jì)算必須使用 2 個(gè)不同的資源進(jìn)行, 每個(gè)有它自己的鎖, 常常沒有選擇只能獲取 2 個(gè)鎖.
獲得多個(gè)鎖可能是危險(xiǎn)的, 然而. 如果你有 2 個(gè)鎖, 稱為 Lock1 和 Lock2, 代碼需要同時(shí)都獲取, 你有一個(gè)潛在的死鎖. 僅僅想象一個(gè)線程鎖住 Lock1 而另一個(gè)同時(shí)獲得 Lock2. 接著每個(gè)線程試圖得到它沒有的那個(gè). 2 個(gè)線程都會(huì)死鎖.
這個(gè)問題的解決方法常常是簡單的: 當(dāng)多個(gè)鎖必須獲得時(shí), 它們應(yīng)當(dāng)一直以同樣順序獲得. 只要遵照這個(gè)慣例, 象上面描述的簡單死鎖能夠避免. 然而, 遵照加鎖順序規(guī)則是做比說難. 非常少見這樣的規(guī)則真正在任何地方被寫下. 常常你能做的最好的是看看別的代碼如何做的.
一些經(jīng)驗(yàn)規(guī)則能幫上忙. 如果你必須獲得一個(gè)對(duì)你的代碼來說的本地鎖(假如, 一個(gè)設(shè)備鎖), 以及一個(gè)屬于內(nèi)核更中心部分的鎖, 先獲取你的. 如果你有一個(gè)旗標(biāo)和自旋鎖的組合, 你必須, 當(dāng)然, 先獲得旗標(biāo); 調(diào)用 down (可能睡眠) 在持有一個(gè)自旋鎖時(shí)是一個(gè)嚴(yán)重的錯(cuò)誤. 但是最重要的, 盡力避免需要多于一個(gè)鎖的情況.
第一個(gè)支持多處理器系統(tǒng)的 Linux 內(nèi)核是 2.0; 它只含有一個(gè)自旋鎖. 這個(gè)大內(nèi)核鎖將整個(gè)內(nèi)核變?yōu)橐粋€(gè)大的臨界區(qū); 在任何時(shí)候只有一個(gè) CPU 能夠執(zhí)行內(nèi)核代碼. 這個(gè)鎖足夠好地解決了并發(fā)問題以允許內(nèi)核開發(fā)者從事所有其他的開發(fā) SMP 所包含的問題. 但是它不是擴(kuò)充地很好. 甚至一個(gè) 2 個(gè)處理器的系統(tǒng)可能花費(fèi)可觀數(shù)量的時(shí)間只是等待這個(gè)大內(nèi)核鎖. 一個(gè) 4 個(gè)處理器的系統(tǒng)的性能甚至不接近 4 個(gè)獨(dú)立的機(jī)器的性能.
因此, 后續(xù)的內(nèi)核發(fā)布已經(jīng)包含了更細(xì)粒度的加鎖. 在 2.2 中, 一個(gè)自旋鎖控制對(duì)塊 I/O 子系統(tǒng)的存取; 另一個(gè)為網(wǎng)絡(luò)而工作, 等等. 一個(gè)現(xiàn)代的內(nèi)核能包含幾千個(gè)鎖, 每個(gè)保護(hù)一個(gè)小的資源. 這種細(xì)粒度的加鎖可能對(duì)伸縮性是好的; 它允許每個(gè)處理器在它自己特定的任務(wù)上工作而不必競爭其他處理器使用的鎖. 很少人忘記大內(nèi)核鎖.[19]
但是, 細(xì)粒度加鎖帶有開銷. 在有幾千個(gè)鎖的內(nèi)核中, 很難知道你需要那個(gè)鎖 -- 以及你應(yīng)當(dāng)以什么順序獲取它們 -- 來進(jìn)行一個(gè)特定的操作. 記住加鎖錯(cuò)誤可能非常難發(fā)現(xiàn); 更多的鎖提供了更多的機(jī)會(huì)使真正有害的加鎖 bug 鉆進(jìn)內(nèi)核中. 細(xì)粒度加鎖能帶來一定水平的復(fù)雜性, 長期來, 對(duì)內(nèi)核的可維護(hù)性有一個(gè)大的, 不利的效果.
在一個(gè)設(shè)備驅(qū)動(dòng)中加鎖常常是相對(duì)直接的; 你可以用一個(gè)鎖來涵蓋你做的所有東西, 或者你可以給你管理的每個(gè)設(shè)備創(chuàng)建一個(gè)鎖. 作為一個(gè)通用的規(guī)則, 你應(yīng)當(dāng)從相對(duì)粗的加鎖開始, 除非你有確實(shí)的理由相信競爭可能是一個(gè)問題. 忍住慫恿去過早地優(yōu)化; 真實(shí)地性能約束常常表現(xiàn)在想不到的地方.
如果你確實(shí)懷疑鎖競爭在損壞性能, 你可能發(fā)現(xiàn) lockmeter 工具有用. 這個(gè)補(bǔ)丁(從 http://oss.sgi.com/projects/lockmeter/ 可得到) 裝備內(nèi)核來測量在鎖等待花費(fèi)的時(shí)間. 通過看這個(gè)報(bào)告, 你能夠很快知道是否鎖競爭真的是問題.
[19] 這個(gè)鎖仍然存在于 2.6, 幾個(gè)它現(xiàn)在覆蓋內(nèi)核非常小的部分. 如果你偶然發(fā)現(xiàn)一個(gè) lock_kernel 調(diào)用, 你已找到了這個(gè)大內(nèi)核鎖. 但是, 想都不要想在任何新代碼中使用它.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: