原文地址: http://hi.baidu.com/_kouu/blog/item/c7f1bcd864bb76f939012f9f.html
Big Kernel Lock(
BKL
)(大內(nèi)核鎖),是linux內(nèi)核中使用到的一種鎖,它跟普通的鎖原理上的一樣的:
lock_kernel();
/* 臨界區(qū) */
unlock_kernel();
但是它又有一些非常詭異的地方。從表面上看:
1、BKL是一個(gè)全局的鎖
(注意,是“一個(gè)”而不是“一種”),它保護(hù)所有使用它來(lái)同步的臨界區(qū)。一旦一個(gè)進(jìn)程獲得BKL,進(jìn)入被它保護(hù)的臨界區(qū),不但該臨界區(qū)被上鎖,所有被它保護(hù)的臨界區(qū)都一起被鎖住。
這看起來(lái)非常之武斷:進(jìn)程A在CPU_1上操作鏈表list_a,而進(jìn)程B在CPU_2上操作全局變量var_b,這兩者本身毫無(wú)瓜葛。但如果你使用了BKL,它們就要“被同步”。
2、BKL是遞歸鎖。
同一進(jìn)程中可以對(duì)BKL嵌套的上鎖和解鎖,當(dāng)解鎖次數(shù)等于上鎖次數(shù)時(shí),鎖才真正被釋放。
這一點(diǎn)雖然跟內(nèi)核中的其他鎖不大一樣,但倒也不算神奇,用戶態(tài)的pthread_mutex也支持這樣的遞歸鎖。
3
、BKL有自動(dòng)釋放的特性。
在CPU_N上,如果當(dāng)前進(jìn)程A持有BKL,則當(dāng)CPU_N上面發(fā)生調(diào)度時(shí),進(jìn)程A持有的BKL將被自動(dòng)釋放。而當(dāng)進(jìn)程A再次被調(diào)度執(zhí)行時(shí),它又自動(dòng)獲得BKL(如果BKL正在被其他進(jìn)程持有,則進(jìn)程A不會(huì)被調(diào)度執(zhí)行)。
這個(gè)特性對(duì)于普通的用戶態(tài)程序來(lái)說(shuō)實(shí)在是不可思議:進(jìn)程A進(jìn)入了臨界區(qū),要準(zhǔn)備修改某全局鏈表list_a,但是由于某種原因而進(jìn)入睡眠(比如系統(tǒng)內(nèi)存緊缺時(shí),等待分配內(nèi)存),結(jié)果鎖就被自動(dòng)釋放了。而另一個(gè)進(jìn)程B就可以堂而皇之的獲得鎖而進(jìn)入臨界區(qū),并且把全局鏈表list_a給改了。等進(jìn)程A從睡眠中被喚醒的時(shí)候,就發(fā)現(xiàn)這個(gè)世界全變了……而鎖呢?竟然完全不起作用。
BKL的前兩個(gè)特性還好理解,第三個(gè)特性實(shí)在是匪夷所思。這么詭異的鎖是怎么來(lái)的呢?
百度一下“大內(nèi)核鎖”可以了解到:據(jù)說(shuō)在linux 2.0時(shí)代,內(nèi)核是不支持SMP(對(duì)稱多處理器)的。在邁入linux 2.2時(shí)代的時(shí)候,SMP逐漸流行起來(lái),內(nèi)核需要對(duì)其進(jìn)行支持了。但是發(fā)現(xiàn),內(nèi)核中的很多代碼在多個(gè)CPU上同時(shí)運(yùn)行的時(shí)候會(huì)存在問(wèn)題。怎么辦呢?最完美的解決辦法當(dāng)然是把所有存在問(wèn)題的地方都找出來(lái),然后給它們分別安排一個(gè)鎖。但是這樣做的話工作量會(huì)很大,為了快速支持SMP,linux內(nèi)核出了狠招,這就是BKL:CPU進(jìn)入內(nèi)核態(tài)時(shí)就上BKL、退出內(nèi)核態(tài)時(shí)釋放。于是,系統(tǒng)中同一時(shí)刻就只有一個(gè)CPU會(huì)處于內(nèi)核態(tài),內(nèi)核代碼就沒(méi)有了“在多個(gè)CPU上同時(shí)運(yùn)行”的問(wèn)題。
這一招很有效,但是顯然很拙劣,內(nèi)核代碼不能在多個(gè)CPU上同時(shí)運(yùn)行,SMP的優(yōu)勢(shì)大打折扣。于是后來(lái)的內(nèi)核版本又逐步逐步的在削減被BKL所保護(hù)的臨界區(qū),以期把它們都消滅干凈。
類似上面的描述在網(wǎng)絡(luò)上比比皆是,但是似乎網(wǎng)絡(luò)上對(duì)于BKL的描述也僅限于此。看完這些,你對(duì)BKL是否感覺(jué)云里霧里的呢?反正我之前看到BKL的時(shí)候?qū)嵲谑窍氩煌ǎ杏X(jué)這些描述沒(méi)講到點(diǎn)子上。還有人說(shuō),BKL保護(hù)的是代碼,而不是數(shù)據(jù)(資源)。這個(gè)說(shuō)法實(shí)在太抽象,抽象到說(shuō)了等于沒(méi)說(shuō)。(有什么代碼是需要保護(hù)的?如果不是因?yàn)榇a跟資源打交道,它還有被保護(hù)的必要?)
為什么要設(shè)計(jì)這樣一種鎖呢?對(duì)于一種特殊的、非傳統(tǒng)的機(jī)制,要理解為什么,要找到它存在的意義,最好的辦法莫過(guò)于:找出一個(gè)場(chǎng)景,在這個(gè)場(chǎng)景下使用這種特殊的機(jī)制比使用其他傳統(tǒng)的機(jī)制更有優(yōu)勢(shì)。比如,為什么要設(shè)計(jì)讀寫鎖?因?yàn)樵谧x寫分明的場(chǎng)景下,使用讀寫鎖可以避免讀讀沖突,減少程序的阻塞。再比如,為什么要設(shè)計(jì)seqlock(見(jiàn)《linux seqlock & rcu 淺析》)?因?yàn)樵谧x寫分明、且讀多寫少寫優(yōu)先的場(chǎng)景下,使用seqlock可以避免寫操作被阻塞。
那么BKL呢?它在什么場(chǎng)景下能帶來(lái)什么樣的優(yōu)勢(shì)呢?網(wǎng)絡(luò)上找不到這樣的描述。我試圖在內(nèi)核代碼中尋找這樣的場(chǎng)景,但是完全找不到。似乎BKL的存在毫無(wú)道理,怎么看,把現(xiàn)有的BKL換成spinlock或者信號(hào)量,都是更好的選擇(這些鎖至少不是全局的)。
要想解釋BKL為什么會(huì)設(shè)計(jì)成這樣子,或許只能從它的誕生時(shí)需要解決的問(wèn)題入手……
想一想,一個(gè)進(jìn)程上了一個(gè)鎖,保護(hù)了一個(gè)臨界區(qū)。那么在鎖被釋放之前,其他進(jìn)程都應(yīng)該遵守規(guī)則,而不進(jìn)入這個(gè)臨界區(qū)。這里的“其他進(jìn)程”有兩層含義:
1、由于調(diào)度,
在同一CPU上交錯(cuò)運(yùn)行的其他進(jìn)程
;
2、由于SMP,
在不同CPU上同時(shí)運(yùn)行的其他進(jìn)程
;
把“其他進(jìn)程”劃分成這兩類,似乎有點(diǎn)畸形,但這也正是BKL畸形之所在。因?yàn)槲覀円话闼f(shuō)的同步、所用到的鎖都是針對(duì)所有“其他進(jìn)程”的,不管是“在同一CPU上交錯(cuò)運(yùn)行的其他進(jìn)程”,還是“在不同CPU上同時(shí)運(yùn)行的其他進(jìn)程”,都應(yīng)該在被同步的范圍之內(nèi)。
在SMP和BKL被引入內(nèi)核之前,內(nèi)核代碼是能夠正常運(yùn)行的,這時(shí)候系統(tǒng)中只有一個(gè)CPU。內(nèi)核中也有一些同步措施,它們的作用是同步“在同一CPU上交叉運(yùn)行的其他進(jìn)程”。
隨著SMP的引入,“在不同CPU上同時(shí)運(yùn)行的其他進(jìn)程”出現(xiàn)了,而B(niǎo)KL的作用就是(且僅僅是)解決它們的同步問(wèn)題。于是,當(dāng)發(fā)生進(jìn)程調(diào)度的時(shí)候,內(nèi)核自動(dòng)把上一個(gè)進(jìn)程持有的BKL釋放。因?yàn)锽KL不關(guān)心“在同一CPU上交叉運(yùn)行的其他進(jìn)程”的同步問(wèn)題,這些問(wèn)題是由原有的那些機(jī)制去保證的。
前面說(shuō)過(guò),我們一般看到的鎖都是同時(shí)支持上面兩類“其他進(jìn)程”的。而B(niǎo)KL只支持其中之一,這就是它不健全的地方。正是因?yàn)檫@種不健全,所以很難想象有什么應(yīng)用場(chǎng)景是我們應(yīng)該去使用BKL的。它并不是一個(gè)可以獨(dú)立使用的東西,它只是一個(gè)補(bǔ)丁,在它的背后必須有另一種同步機(jī)制的支持,這種機(jī)制必須足以應(yīng)付“在同一CPU上交叉運(yùn)行的其他進(jìn)程”帶來(lái)的同步問(wèn)題。
那么BKL為什么不能設(shè)計(jì)成跟普通的鎖一樣健全呢?因?yàn)椋怯捎贐KL的不健全,一個(gè)持有BKL的進(jìn)程在進(jìn)入睡眠之后,其它進(jìn)程還可以持有BKL,還可以進(jìn)入臨界區(qū)。如果它健全的話,其他進(jìn)程都只有傻傻等待的份,SMP的優(yōu)勢(shì)將再打折扣。
再具體一點(diǎn)看看,當(dāng)SMP引入內(nèi)核之后,產(chǎn)生了兩個(gè)問(wèn)題:
1、原先那些針對(duì)“在同一CPU上交叉運(yùn)行的其他進(jìn)程”的鎖需要升級(jí),要支持“在不同CPU上同時(shí)運(yùn)行的其他進(jìn)程”。
這一點(diǎn)是顯而易見(jiàn)的,上面已經(jīng)討論過(guò)了,不能同時(shí)支持兩類“其他進(jìn)程”的鎖是不健全的鎖。并且這個(gè)問(wèn)題也很好解決,把鎖的實(shí)現(xiàn)改一改就好了,CPU必定會(huì)提供SMP環(huán)境下的相應(yīng)的指令支持。
2、解決了第一個(gè)問(wèn)題還不夠。有這樣一些地方,原先并不存在“在同一CPU上交叉運(yùn)行的其他進(jìn)程”的可能性,因此被認(rèn)為不需要同步,或者說(shuō)已經(jīng)隱含了這樣的同步關(guān)系。
在內(nèi)核中,沒(méi)有內(nèi)核搶占(暫時(shí)禁用或不支持)且與中斷處理程序沒(méi)有關(guān)系的代碼,在單CPU情況下,如果它不主動(dòng)讓出CPU,那么就可以認(rèn)為不會(huì)有其他進(jìn)程與它交叉運(yùn)行。
有了這樣的隱含的同步關(guān)系,也就不需要進(jìn)行顯式的同步。但是SMP的出現(xiàn)打破了這一論斷,因?yàn)槌霈F(xiàn)了“在不同CPU上同時(shí)運(yùn)行的其他進(jìn)程”,原本隱含的同步關(guān)系已經(jīng)不能代表同步關(guān)系的全部,所以,其中的一些地方可能還是需要顯式的進(jìn)行同步。BKL就是針對(duì)這些地方而產(chǎn)生的。
總的說(shuō)來(lái),BKL就是為了在那些原本已經(jīng)隱含了單CPU下的同步關(guān)系的地方打一個(gè)補(bǔ)丁,以確保這些地方在SMP環(huán)境下也不會(huì)出現(xiàn)問(wèn)題。反過(guò)來(lái),不健全的BKL之所以能夠工作,是因?yàn)楸澈箅[含了單CPU下的同步關(guān)系。
然而正如文章開(kāi)頭說(shuō)到的,這個(gè)問(wèn)題是有完美解決方案的,把這些因?yàn)镾MP而需要同步的地方都找出來(lái),然后一一安排合適的同步機(jī)制(這些同步機(jī)制能同時(shí)解決單CPU和SMP下的同步問(wèn)題)。BKL只不過(guò)是一種臨時(shí)方案。
由于BKL的不健全,邏輯上有很詭異的地方,內(nèi)核代碼越來(lái)越復(fù)雜,牽涉到BKL的地方更是越來(lái)越難以維護(hù)。再加上BKL對(duì)SMP的限制較大。所以一直以來(lái)內(nèi)核開(kāi)發(fā)者們對(duì)BKL深惡痛絕,并且一再努力削減。(當(dāng)然,請(qǐng)神容易送神難。大內(nèi)核鎖的全局性和遞歸性使得調(diào)用它的代碼很難被理清。)
終于,BKL據(jù)說(shuō)要在2.6.37版本的內(nèi)核代碼中被完全消滅了。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
