Linux内核RCU(Read Copy Update)锁简析
在非常早曾經(jīng),大概是2009年的時候。寫過一篇關(guān)于Linux RCU鎖的文章《RCU鎖在linux內(nèi)核的演變》,如今我承認。那個時候我盡管懂了RCU鎖,可是我沒有能力用一種非常easy的描寫敘述把Linux的實現(xiàn)給展示出來,有道是你能給別人用你自己的方式非常簡潔地描寫敘述清楚,你才是真正的精通它。否則,無異于背誦。換個說法,假設(shè)你在被面試。在短時間內(nèi)靠嘴說給面試官,且他還要能聽明白,就說明自己真的懂了,這樣的時候,是不會給你機會分析源碼的,也不可能讓你背誦源碼。
?????? 時隔五年多,最近又碰到了這個話題,我不能自詡自己對RCU鎖是多么精通,可是起碼,和2009年相比,我確實有所進步,因此在這個臺風肆虐的次日,我嘗試著用我自己的方式描寫敘述一下Linux對RCU鎖的一種實現(xiàn)方式,作為《RCU鎖在linux內(nèi)核的演變》這篇文章的補充。本文不配圖,沒代碼,僅僅是文字。
聲明:假設(shè)你還不知道RCU鎖是什么。請自行baidu,本文不再贅述概念。可是這也就等于說,假設(shè)我自己有一天忘記了RCU。我也不能指望從本文中得到不論什么幫助。我總是這樣,不是嗎?能力在忘不在記。得其義而忘其形。
RCU要素
RCU鎖的要素包含讀標志
假設(shè)一個Reader企圖占領(lǐng)一把RCU鎖,它是不須要付出不論什么代價的,僅僅須要設(shè)置一個標志,讓外界知道有Reader在占領(lǐng)這把RCU鎖。多個Reader能夠共同持有一把RCU鎖。寫時拷貝
假設(shè)有一個Write企圖更新RCU鎖所保護的數(shù)據(jù),那么它會首先查看該RCU鎖的讀標志,假設(shè)有該標志,說明有最少一個Reader持有了該RCU鎖。它須要對原始數(shù)據(jù)make a copy,寫這個副本并將更新過的副本保存在某處,等待時機用該副本更新原始數(shù)據(jù)。更新時機
這個時機就是用副本更新原始數(shù)據(jù)的時間點。這個時間點怎樣確定是RCU鎖實現(xiàn)的算法核心,它直接能夠確定全部的數(shù)據(jù)結(jié)構(gòu)。確切來講,Writer必須waitting for all readers leaving,方可Update原始數(shù)據(jù),問題是,它是怎么知道全部的Reader都離開了呢?Linux內(nèi)核對RCU鎖的幾種實現(xiàn)
1.原始實現(xiàn)-利用搶占禁止
Linux內(nèi)核于2.6內(nèi)核引入了RCU鎖的概念。在第一個版本號中,它利用了搶占禁止的方式來標志有Reader持有RCU鎖,這意味著期間不能發(fā)生task切換(指的是task_struct所代表的sched entity切換)。那么全部Reader均已經(jīng)釋放RCU鎖的標志就是。task切換了,因此非常easy,用副本Update原始數(shù)據(jù)的時機就是task切換時。
?????? 全部的Write會將自己寫的副本掛在一個list上,在task切換的時候會touch這個list,假設(shè)該list非空,則遍歷每個元素。Update原始數(shù)據(jù)。
評價[該部分與實現(xiàn)無關(guān),純形而上的,能夠忽略] ?????
這就是第一版的原始實現(xiàn)。它是否合理姑且不論。確實。它能夠工作。可是:a.它這樣的實現(xiàn)是否會影響調(diào)度子系統(tǒng)的時延
b.因為禁用搶占,搶占粒度變粗,對交互性是否會有影響
c.對CPU間的task負載均衡的影響呢
我們發(fā)現(xiàn),因為RCU的這個實現(xiàn)不是靠自身機制實現(xiàn)的,它不可避免地會影響到系統(tǒng)的核心機制。比方調(diào)度,負載均衡等,這意味著它不能長久。也無法經(jīng)歷復雜的演變。因為隨著它在這條路上的逐步演進,對系統(tǒng)核心機制的影響將越來越大,故而,它必須從系統(tǒng)層面剝離出來。確實,它也是這么做的。這就是第二代RCU實現(xiàn)-可搶占RCU鎖。
2.新實現(xiàn)-利用階段計數(shù)器
須要一種更加有效的方式來標志Reader已經(jīng)持有鎖-第一要素讀標志,而且這個標志要盡可能精確,且不能使用系統(tǒng)核心的機制,要做成全然封閉的閉環(huán),不依靠外部當然也就不會影響外部。?????? 純天然的想法就是使用計數(shù)器,每一把RCU持有一個Reader計數(shù)器,一旦有Reader前來持鎖。僅僅須要一個原子操作,將該計數(shù)器加1就可以,Writer寫數(shù)據(jù)時。發(fā)現(xiàn)計數(shù)器不是0就意味著須要make a copy了-第二要素寫時拷貝(COW-Copy On Write)。
如今的問題是第三要素,Writer怎么知道全部的Reade都已經(jīng)將鎖釋放了呢??
?????? 純天然的想法就是在某個Reader釋放鎖的時候,計數(shù)器減1。當計數(shù)器又一次變?yōu)?的時候。這就是副本更新原始數(shù)據(jù)的時機。
確實是這樣,可是依照持鎖和解鎖的分布看,它們應(yīng)該是均等的,這意味著計數(shù)器的值會在一個期望值上下波動,變成0的希望及其渺茫,因此須要引入還有一個參量,即階段。
?????? 將唯一的那個RCU計數(shù)器分裂為兩個計數(shù)器:old readers和new readers。
?????? 太初,任選某一個時刻,將RCU鎖當前的計數(shù)器(稱為原始計數(shù)器)值復制一份存入old readers,計數(shù)器清0,原始計數(shù)器改稱為new readers。復制結(jié)束的當下,new readers計數(shù)器為0,old readers計數(shù)器為現(xiàn)階段持有鎖的reader的數(shù)量。而且持鎖者task(即task_struct)與RCU鎖之間保持關(guān)聯(lián)(難道不是一個task_struct字段能夠搞定的嗎?)。task永遠知道自己是new reader還是old reader。
?????? 此時,就能夠明白定義lock和unlock的行為了:
lock--設(shè)置自己的task為new reader,將RCU的new reader計數(shù)器加1。
unlock--獲取自己的task是new reader還是old reader,將自己所在的reader計數(shù)器減1。
此時非常明白的事實是。old reader計數(shù)器總是會遞減而不會遞增,而new reader不但會遞增也會遞減。這樣,選擇Update的時機也非常明白了,那就是,old reader計數(shù)器變?yōu)?,這個時刻,就該將全部的副本覆蓋原始數(shù)據(jù)了。
?????? 如今總結(jié)全部的三個要素:
讀標志
為該RCU鎖的new reader計數(shù)器加1寫時拷貝
假設(shè)該RCU鎖的old reader計數(shù)器不為0,則運行寫時復制。更新時機
每次unlock操作,都會將本task的reader計數(shù)器(或者是new reader。或者是old reader)減1。一旦該RCU鎖的old reader計數(shù)器變成0。則運行全部的Update操作。評價[該部分與實現(xiàn)無關(guān),純形而上的。能夠忽略]
持有RCU鎖的reader。能夠睡眠,能夠被搶占。能夠調(diào)度到別的CPU上,全然是封閉的,和系統(tǒng)其他的機制無關(guān)。然而。我一直在思考一個更好的實現(xiàn),僅僅因瘋子不給力。!
3.RCU Tree實現(xiàn)(實在是沒有2好)
今天實在沒有時間了,要出去。興許補充。轉(zhuǎn)載于:https://www.cnblogs.com/wzjhoutai/p/7202772.html
總結(jié)
以上是生活随笔為你收集整理的Linux内核RCU(Read Copy Update)锁简析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到自己卖好多鸡蛋好不好
- 下一篇: Linux 命令行输入