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