0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深入理解Linux RCU:RCU是讀寫(xiě)鎖的替代者

Linux閱碼場(chǎng) ? 來(lái)源:未知 ? 作者:李倩 ? 2018-05-10 09:13 ? 次閱讀

一、

RCU的用法

RCU最常用的目的是替換已有的機(jī)制,如下所示:

讀寫(xiě)鎖

受限制的引用計(jì)數(shù)機(jī)制

批量引用計(jì)數(shù)機(jī)制

窮人版的垃圾回收器

存在擔(dān)保

類(lèi)型安全的內(nèi)存

等待事物結(jié)束

1.1 RCU是讀寫(xiě)鎖的替代者

Linux內(nèi)核中,RCU最常見(jiàn)的用途是替換讀寫(xiě)鎖。在20世紀(jì)90年代初期,Paul在實(shí)現(xiàn)通用RCU之前,實(shí)現(xiàn)了一種輕量級(jí)的讀寫(xiě)鎖。后來(lái),為這個(gè)輕量級(jí)讀寫(xiě)鎖原型所設(shè)想的每個(gè)用途,最終都使用RCU來(lái)實(shí)現(xiàn)了。

RCU和讀寫(xiě)鎖最關(guān)鍵的相似之處,在于兩者都有可以并行執(zhí)行讀端臨界區(qū)。事實(shí)上,在某些情況下,完全可以用對(duì)應(yīng)的讀寫(xiě)鎖API來(lái)替換RCU的API,反之亦然。

RCU的優(yōu)點(diǎn)在于:性能、不會(huì)死鎖,以及良好的實(shí)時(shí)延遲。當(dāng)然RCU也有一點(diǎn)缺點(diǎn),比如:讀者與更新者并發(fā)執(zhí)行,低優(yōu)先級(jí)RCU讀者也可以阻塞正等待優(yōu)雅周期(Grace Period)結(jié)束的高優(yōu)先級(jí)線程,優(yōu)雅周期的延遲可能達(dá)到好幾毫秒。

上圖是不是略顯奇怪,RCU讀端延遲竟然小于一個(gè)CPU周期?這不是開(kāi)玩笑,因?yàn)橛心承?shí)現(xiàn)中(例如,服務(wù)器Linux),RCU讀端就完全是一個(gè)空操作。當(dāng)然,在這樣的實(shí)現(xiàn)中,它可能會(huì)包含一個(gè)編譯屏障,因此也會(huì)對(duì)性能產(chǎn)生那么一點(diǎn)點(diǎn)影響。

請(qǐng)注意,在單個(gè)CPU上讀寫(xiě)鎖比RCU慢一個(gè)數(shù)量級(jí),在16個(gè)CPU上讀寫(xiě)鎖比RCU幾乎要慢兩個(gè)數(shù)量級(jí)。隨著CPU數(shù)量的增加,RCU的擴(kuò)展性優(yōu)勢(shì)越來(lái)越突出??梢赃@么說(shuō),RCU幾乎就是水平擴(kuò)展,這可以在上圖中看出來(lái)。

當(dāng)內(nèi)核配置了CONFIG_PREEMPT的時(shí)候,RCU仍然超過(guò)了讀寫(xiě)鎖一到三個(gè)數(shù)量級(jí),如下圖所示。請(qǐng)注意:讀寫(xiě)鎖在CPU數(shù)目很多時(shí)的陡峭曲線。

當(dāng)然,上圖中的臨界區(qū)長(zhǎng)度為0,這夸大了讀寫(xiě)鎖的性能劣勢(shì)。隨著臨界區(qū)的加大,RCU的性能優(yōu)勢(shì)也不再顯著。在下圖中,有16個(gè)CPU,y軸代表讀端原語(yǔ)的總開(kāi)銷(xiāo),x軸代表臨界區(qū)長(zhǎng)度。

然而,一般說(shuō)來(lái),臨界區(qū)都能在幾個(gè)毫秒內(nèi)完成,所以總的來(lái)說(shuō),在性能方面,測(cè)試結(jié)果對(duì)RCU是有利的。

另外,RCU讀端原語(yǔ)基本上是不會(huì)死鎖的。因?yàn)樗旧砭蛯儆跓o(wú)鎖編程的范疇。

這種免于死鎖的能力來(lái)源于RCU的讀端原語(yǔ)不阻塞、不自旋,甚至不存在向后跳轉(zhuǎn)語(yǔ)句,所以RCU讀端原語(yǔ)的執(zhí)行時(shí)間是確定的。這使得RCU讀端原語(yǔ)不可能形成死鎖循環(huán)。

RCU讀端免于死鎖的能力帶來(lái)了一個(gè)有趣的后果:RCU讀者可以恣意地升級(jí)為RCU更新者。在讀寫(xiě)鎖中嘗試這種升級(jí)則會(huì)有可能造成死鎖。進(jìn)行RCU讀者到更新者升級(jí)的代碼片段如下所示:

1 rcu_read_lock();

2 list_for_each_entry_rcu(p, &head, list_field) {

3 do_something_with(p);

4 if (need_update(p)) {

5 spin_lock(my_lock);

6 do_update(p);

7 spin_unlock(&my_lock);

8 }

9 }

10 rcu_read_unlock();

請(qǐng)注意,do_update()是在鎖的保護(hù)下執(zhí)行,也是在RCU讀端的保護(hù)下執(zhí)行。

RCU免于死鎖的特性帶來(lái)的另一個(gè)有趣后果是RCU不會(huì)受優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題影響。比如,低優(yōu)先級(jí)的RCU讀者無(wú)法阻止高優(yōu)先級(jí)的RCU更新者獲取更新端鎖。類(lèi)似地,低優(yōu)先級(jí)的更新者也無(wú)法阻止高優(yōu)先級(jí)的RCU讀者進(jìn)入RCU讀端臨界區(qū)。

另一方面,因?yàn)镽CU讀端原語(yǔ)既不自旋也不阻塞,所以這些原語(yǔ)有著極佳的實(shí)時(shí)延遲。而自旋鎖或者讀寫(xiě)鎖都存在不確定的實(shí)時(shí)延遲。

但是,RCU還是會(huì)受到更隱晦的優(yōu)先級(jí)反轉(zhuǎn)問(wèn)題影響,比如,在等待RCU優(yōu)雅周期結(jié)束而阻塞的高優(yōu)先級(jí)進(jìn)程,會(huì)被-rt內(nèi)核的低優(yōu)先級(jí)RCU讀者阻塞。這可以用RCU優(yōu)先級(jí)提升來(lái)解決。

再一方面,因?yàn)镽CU讀者既不自旋也不阻塞,RCU更新者也沒(méi)有任何類(lèi)似回滾或者中止的語(yǔ)義,所以RCU讀者和更新者可以并發(fā)執(zhí)行。這意味著RCU讀者有可能訪問(wèn)舊數(shù)據(jù),還有可能發(fā)現(xiàn)數(shù)據(jù)不一致,無(wú)論這兩個(gè)問(wèn)題中的哪一個(gè),都讓讀寫(xiě)鎖有卷土重來(lái)的機(jī)會(huì)。

不過(guò),令人吃驚的是,在大量情景中,數(shù)據(jù)不一致和舊數(shù)據(jù)都不是問(wèn)題。網(wǎng)絡(luò)路由表是一個(gè)經(jīng)典例子。因?yàn)槁酚傻母驴赡芤ㄏ喈?dāng)長(zhǎng)一段時(shí)間才能到達(dá)指定系統(tǒng)(幾秒甚至幾分鐘),所以系統(tǒng)可能會(huì)在新數(shù)據(jù)到來(lái)后的一段時(shí)間內(nèi),仍然將報(bào)文發(fā)到錯(cuò)誤的地址去。通常,在幾毫秒內(nèi)將報(bào)文發(fā)送到錯(cuò)誤地址并不算什么問(wèn)題。

簡(jiǎn)單地說(shuō),讀寫(xiě)鎖和RCU提供了不同的保證。在讀寫(xiě)鎖中,任何在寫(xiě)者之后開(kāi)始的讀者都“保證”能看到新值。與之相反,在RCU中,在更新者完成后才開(kāi)始的讀者都“保證”能看見(jiàn)新值,在更新者開(kāi)始后才完成的讀者有可能看見(jiàn)新值,也有可能看見(jiàn)舊值,這取決于具體的時(shí)機(jī)。

在實(shí)時(shí)RCU、SRCU或QRCU中,被搶占的讀者將阻止正在進(jìn)行中的優(yōu)雅周期的完成,即使有高優(yōu)先級(jí)的任務(wù)在等待優(yōu)雅周期完成時(shí)也是如此。實(shí)時(shí)RCU可以通過(guò)用call_rcu()替換synchronize_rcu()來(lái)避免此問(wèn)題,或者采用RCU優(yōu)先級(jí)提升來(lái)避免。

除了那些“玩具”RCU實(shí)現(xiàn),RCU優(yōu)雅周期可能會(huì)延續(xù)好幾個(gè)毫秒。這使得RCU更適于使用在讀數(shù)據(jù)占多數(shù)的情景。

將讀寫(xiě)鎖轉(zhuǎn)換成RCU非常簡(jiǎn)單,如下:

1.2 RCU是一種受限制的引用計(jì)數(shù)機(jī)制

因?yàn)閮?yōu)雅周期不能在RCU讀端臨界區(qū)進(jìn)行時(shí)結(jié)束,所以RCU讀端原語(yǔ)可以像受限的引用計(jì)數(shù)機(jī)制一樣使用。比如考慮下面的代碼片段:

1 rcu_read_lock(); /* acquire reference. */

2 p = rcu_dereference(head);

3 /* do something with p. */

4 rcu_read_unlock(); /* release reference. */

rcu_read_lock()原語(yǔ)可以看作是獲取對(duì)p的引用,因?yàn)橄鄳?yīng)的優(yōu)雅周期無(wú)法在配對(duì)的rcu_read_unlock()之前結(jié)束。這種引用計(jì)數(shù)機(jī)制是受限制的,因?yàn)槲覀儾辉试S在RCU讀端臨界區(qū)中阻塞,也不允許將一個(gè)任務(wù)的RCU讀端臨界區(qū)傳遞給另一個(gè)任務(wù)。

不管上述的限制,下列代碼可以安全地刪除p:

1 spin_lock(&mylock);

2 p = head;

3 rcu_assign_pointer(head, NULL);

4 spin_unlock(&mylock);

5 /* Wait for all references to be released. */

6 synchronize_rcu();

7 kfree(p);

當(dāng)然,RCU也可以與傳統(tǒng)的引用計(jì)數(shù)結(jié)合。但是為什么不直接使用引用計(jì)數(shù)?部分原因是性能,如下圖所示,圖中顯示了在16個(gè)3GHz CPU的Intel x86系統(tǒng)中采集的數(shù)據(jù)。

和讀寫(xiě)鎖一樣,RCU的性能優(yōu)勢(shì)主要來(lái)源于較短的臨界區(qū),如下圖所示。

1.3 RCU是一種可大規(guī)模使用的引用計(jì)數(shù)機(jī)制

如前所述,傳統(tǒng)的引用計(jì)數(shù)通常與某種或者一組數(shù)據(jù)結(jié)構(gòu)有聯(lián)系。然而,維護(hù)一組數(shù)據(jù)結(jié)構(gòu)的全局引用計(jì)數(shù),通常會(huì)導(dǎo)致包含引用計(jì)數(shù)的緩存行來(lái)回“乒乓”。這種緩存行“乒乓”會(huì)嚴(yán)重影響系統(tǒng)性能。

相反,RCU的輕量級(jí)讀端原語(yǔ)允許讀端極其頻繁地調(diào)用,卻只帶來(lái)微不足道的性能影響,這使得RCU可以作為一種幾乎沒(méi)有性能損失的“批量引用計(jì)數(shù)機(jī)制”。當(dāng)某個(gè)任務(wù)需要在一大段代碼中持有引用時(shí),可以使用可睡眠RCU(SRCU)。但是,一個(gè)任務(wù)不能將引用鎖引用傳遞給另一個(gè)任務(wù)。例如:在開(kāi)始一次I/O時(shí)獲取引用,然后當(dāng)對(duì)應(yīng)的I/O完成時(shí)在中斷處理函數(shù)里釋放該引用。

1.4 RCU是窮人版的垃圾回收器

當(dāng)人們剛開(kāi)始學(xué)習(xí)RCU時(shí),有種比較少見(jiàn)的感嘆是“RCU有點(diǎn)像垃圾回收器!”。這種感嘆有一部分是對(duì)的,不過(guò)還是會(huì)給學(xué)習(xí)造成誤導(dǎo)。

也許思考RCU與垃圾自動(dòng)回收器(GC)之間關(guān)系的最好辦法是,RCU類(lèi)似自動(dòng)決定回收時(shí)機(jī)的GC,但是RCU與GC有幾點(diǎn)不同:(1)程序員必須手動(dòng)指示何時(shí)可以回收指定數(shù)據(jù)結(jié)構(gòu),(2)程序員必須手動(dòng)標(biāo)出可以合法持有引用的RCU讀端臨界區(qū)。

盡管存在這些差異,兩者的相似程度仍然相當(dāng)高,至少有一篇理論分析RCU的文獻(xiàn)曾經(jīng)分析過(guò)兩者的相似度。

1.5 RCU是一種提供存在擔(dān)保的方法

通過(guò)鎖來(lái)提供存在擔(dān)保有其不利之處。與鎖類(lèi)似,如果任何受RCU保護(hù)的數(shù)據(jù)元素在RCU讀端臨界區(qū)中被訪問(wèn),那么數(shù)據(jù)元素在RCU讀端臨界區(qū)持續(xù)期間保證存在。

1 int delete(int key)

2 {

3 struct element *p;

4 int b;

5 5

6 b = hashfunction(key);

7 rcu_read_lock();

8 p = rcu_dereference(hashtable[b]);

9 if (p == NULL || p->key != key) {

10 rcu_read_unlock();

11 return 0;

12 }

13 spin_lock(&p->lock);

14 if (hashtable[b] == p && p->key == key) {

15 rcu_read_unlock();

16 hashtable[b] = NULL;

17 spin_unlock(&p->lock);

18 synchronize_rcu();

19 kfree(p);

20 return 1;

21 }

22 spin_unlock(&p->lock);

23 rcu_read_unlock();

24 return 0;

25 }

上圖展示了基于RCU的存在擔(dān)保如何通過(guò)從哈希表刪除元素的函數(shù)來(lái)實(shí)現(xiàn)每數(shù)據(jù)元素鎖。第6行計(jì)算哈希函數(shù),第7行進(jìn)入RCU讀端臨界區(qū)。如果第9行發(fā)現(xiàn)哈希表對(duì)應(yīng)的哈希項(xiàng)(bucket)為空,或者數(shù)據(jù)元素不是我們想要?jiǎng)h除的那個(gè),那么第10行退出RCU讀端臨界區(qū),第11行返回錯(cuò)誤。

如果第9行判斷為false,第13行獲取更新端的自旋鎖,然后第14行檢查元素是否還是我們想要的。如果是,第15行退出RCU讀端臨界區(qū),第16行從哈希表中刪除找到的元素,第17行釋放鎖,第18行等待所有之前已經(jīng)存在的RCU讀端臨界區(qū)退出,第19行釋放剛被刪除的元素,最后第20行返回成功。如果14行的判斷發(fā)現(xiàn)元素不再是我們想要的,那么第22行釋放鎖,第23行退出RCU讀端臨界區(qū),第24行返回錯(cuò)誤以刪除該關(guān)鍵字。

1.6 RCU是一種提供類(lèi)型安全內(nèi)存的方法

很多無(wú)鎖算法并不需要數(shù)據(jù)元素在被RCU讀端臨界區(qū)引用時(shí)保持完全一致,只要數(shù)據(jù)元素的類(lèi)型不變就可以了。換句話說(shuō),只要數(shù)據(jù)結(jié)構(gòu)類(lèi)型不變,無(wú)鎖算法可以允許某個(gè)數(shù)據(jù)元素在被其他對(duì)象引用時(shí)被釋放并重新分配,但是決不允許類(lèi)型上的改變。這種“保證”,在學(xué)術(shù)文獻(xiàn)中被稱(chēng)為“類(lèi)型安全的內(nèi)存”,它比前一節(jié)提到的存在擔(dān)保要弱一些,因此處理起來(lái)也要困難一些。類(lèi)型安全的內(nèi)存算法在Linux內(nèi)核中的應(yīng)用是slab緩存,被SLAB_DESTROY_BY_RCU標(biāo)記專(zhuān)門(mén)標(biāo)出來(lái)的緩存通過(guò)RCU將已釋放的slab返回給系統(tǒng)內(nèi)存。在任何已有的RCU讀端臨界區(qū)持續(xù)期間,使用RCU可以保證所有帶有SLAB_DESTROY_BY_RCU標(biāo)記且正在使用的slab元素仍然在該slab中,類(lèi)型保持一致。

雖然基于類(lèi)型安全的無(wú)鎖算法在特定情景下非常有效,但是最好還是盡量使用存在擔(dān)保。畢竟簡(jiǎn)單總是更好的。

1.7 RCU是一種等待事物結(jié)束的方式

RCU的一個(gè)強(qiáng)大之處,就是允許你在等待上千個(gè)不同事物結(jié)束的同時(shí),又不用顯式地去跟蹤其中每一個(gè)事物,因此也就無(wú)需擔(dān)心性能下降、擴(kuò)展限制、復(fù)雜的死鎖場(chǎng)景、內(nèi)存泄露等顯式跟蹤機(jī)制本身的問(wèn)題。

下面展示如何實(shí)現(xiàn)與不可屏蔽中斷(NMI)處理函數(shù)的交互,如果用鎖來(lái)實(shí)現(xiàn),這將極其困難。步驟如下:

1.做出改變,比如,OS對(duì)一個(gè)NMI做出反應(yīng)。在NMI中使用RCU讀端原語(yǔ)。

2.等待所有已有的讀端臨界區(qū)完全退出(比如使用synchronize_sched()原語(yǔ))。

3.掃尾工作,比如,返回表明改變成功完成的狀態(tài)。

下面是一個(gè)Linux內(nèi)核中的例子。在這個(gè)例子中,timer_stop()函數(shù)使用synchronize_sched()確保在釋放相關(guān)資源之前,所有正在處理的NMI處理函數(shù)已經(jīng)完成。

1 struct profile_buffer {

2 long size;

3 atomic_t entry[0];

4 };

5 static struct profile_buffer *buf = NULL;

6

7 void nmi_profile(unsigned long pcvalue)

8 {

9 struct profile_buffer *p =

rcu_dereference(buf);

10

11 if (p == NULL)

12 return;

13 if (pcvalue >= p->size)

14 return;

15 atomic_inc(&p->entry[pcvalue]);

16 }

17

18 void nmi_stop(void)

19 {

20 struct profile_buffer *p = buf;

21

22 if (p == NULL)

23 return;

24 rcu_assign_pointer(buf, NULL);

25 synchronize_sched();

26 kfree(p);

27 }

第1到4行定義了profile_buffer結(jié)構(gòu),包含一個(gè)大小和一個(gè)變長(zhǎng)數(shù)組的入口。第5行定義了指向profile_buffer的指針,這里假設(shè)別處對(duì)該指針進(jìn)行了初始化,指向內(nèi)存的動(dòng)態(tài)分配區(qū)。

第7至16行定義了nmi_profile()函數(shù),供NMI中斷處理函數(shù)調(diào)用。該函數(shù)不會(huì)被搶占,也不會(huì)被普通的中斷處理函數(shù)打斷,但是,該函數(shù)還是會(huì)受高速緩存未命中、ECC錯(cuò)誤以及被同一個(gè)核的其他硬件線程搶占時(shí)鐘周期等因素影響。第9行使用rcu_dereference()原語(yǔ)來(lái)獲取指向profile_buffer的本地指針,這樣做是為了確保在DECAlpha上的內(nèi)存順序執(zhí)行,如果當(dāng)前沒(méi)有分配profile_buffer,第11行和12行退出,如果參數(shù)pcvalue超出范圍,第13和14行退出。否則,第15行增加以參數(shù)pcvalue為下標(biāo)的profile_buffer項(xiàng)的值。請(qǐng)注意,profile_buffer結(jié)構(gòu)中的size保證了pcvalue不會(huì)超出緩沖區(qū)的范圍,即使突然將較大的緩沖區(qū)替換成了較小的緩沖區(qū)也是如此。

第18至27行定義了nmi_stop()函數(shù),由調(diào)用者負(fù)責(zé)互斥訪問(wèn)(比如持有正確的鎖)。第20行獲取profile_buffer的指針,如果緩沖區(qū)為空,第22和23行退出。否則,第24行將profile_buffer的指針置NULL(使用rcu_assign_pointer()原語(yǔ)在弱順序的機(jī)器中保證內(nèi)存順序訪問(wèn)),第25行等待RCU Sched的優(yōu)雅周期結(jié)束,尤其是等待所有不可搶占的代碼——包括NMI中斷處理函數(shù)——結(jié)束。一旦執(zhí)行到第26行,我們就可以保證所有獲取到指向舊緩沖區(qū)指針的nmi_profile()實(shí)例都已經(jīng)返回了?,F(xiàn)在可以安全釋放緩沖區(qū),這時(shí)使用kfree()原語(yǔ)。

簡(jiǎn)而言之,RCU讓profile_buffer動(dòng)態(tài)切換變得更簡(jiǎn)單,你可以試試原子操作,也可以用鎖來(lái)折磨下自己。注意考慮到如下一點(diǎn):在大多數(shù)CPU架構(gòu)中,原子操作和鎖都可能存在循環(huán)語(yǔ)句,在循環(huán)的過(guò)程中可能會(huì)被NMI中斷。

二、RCU API

2.1等待完成的API族

“RCU Classic”一列對(duì)應(yīng)的是RCU的原始實(shí)現(xiàn),rcu_read_lock()和rcu_read_unlock()原語(yǔ)標(biāo)示出RCU讀端臨界區(qū),可以嵌套使用。對(duì)應(yīng)的同步的更新端原語(yǔ)synchronize_rcu()和synchronize_net()都是等待當(dāng)前正在執(zhí)行的RCU讀端臨界區(qū)退出。等待時(shí)間被稱(chēng)為“優(yōu)雅周期”。異步的更新端原語(yǔ)call_rcu()在后續(xù)的優(yōu)雅周期結(jié)束后調(diào)用由參數(shù)指定的函數(shù)。比如,call_rcu(p, f);在優(yōu)雅周期結(jié)束后執(zhí)行RCU回調(diào)f(p)。

想要利用基于RCU的類(lèi)型安全的內(nèi)存,要將SLAB_DESTROY_BY_RCU傳遞給kmem_cache_create()。有一點(diǎn)很重要,SLAB_DESTROY_BY_RCU不會(huì)阻止kmem_cache_alloc()立即重新分配剛被kmem_cache_free()釋放的內(nèi)存!事實(shí)上,由rcu_dereference返回的受SLAB_DESTROY_ BY_RCU標(biāo)記保護(hù)的數(shù)據(jù)結(jié)構(gòu)可能會(huì)釋放——重新分配任意次,甚至在rcu_read_lock()保護(hù)下也是如此。但是,SLAB_DESTROY_BY_RCU可以阻止kmem_cache_free()在RCU優(yōu)雅周期結(jié)束之前,它所返回?cái)?shù)據(jù)結(jié)構(gòu)的完全釋放給SLAB。一句話,雖然數(shù)據(jù)元素可能被釋放——重新分配N(xiāo)次,但是它的類(lèi)型是保持不變的。

2.2訂閱和版本維護(hù)API

表中的第一類(lèi)API作用于Linux的struct list_head循環(huán)雙鏈表。list_for_each_ entry_rcu()原語(yǔ)以類(lèi)型安全的方式遍歷受RCU保護(hù)的鏈表。在非Alpha的平臺(tái)上,該原語(yǔ)相較于list_for_each_entry()原語(yǔ)不產(chǎn)生或者只帶來(lái)極低的性能懲罰。list_add_rcu()、list_add_tail_rcu()和list_replace_rcu()原語(yǔ)都是對(duì)非RCU版本的模擬,但是在弱順序的機(jī)器上回帶來(lái)額外的內(nèi)存屏障開(kāi)銷(xiāo)。list_del_rcu()原語(yǔ)同樣是非RCU版本的模擬,但是奇怪的是它要比非RCU版本快一點(diǎn),這是由于list_del_rcu()只毒化prev指針,而list_del()會(huì)同時(shí)毒化prev和next指針。最后,list_splice_init_rcu()原語(yǔ)和它的非RCU版本類(lèi)似,但是會(huì)帶來(lái)一個(gè)完整的優(yōu)雅周期的延遲。

表中的第二類(lèi)API直接作用于Linux的struct hlist_head線性哈希表。struct hlist_head比structlist_head高級(jí)一點(diǎn)的地方是前者只需要一個(gè)單指針的鏈表頭部,這在大型哈希表中將節(jié)省大量?jī)?nèi)存。表中的struct hlist_head原語(yǔ)與非RCU版本的關(guān)系同struct list_head原語(yǔ)的類(lèi)似關(guān)系一樣。

表中的最后一類(lèi)API直接作用于指針,這對(duì)創(chuàng)建受RCU保護(hù)的非鏈表數(shù)據(jù)元素非常有用,比如受RCU保護(hù)的數(shù)組和樹(shù)。rcu_assign_pointer()原語(yǔ)確保在弱序機(jī)器上,任何在給指針賦值之前進(jìn)行的初始化都將按照順序執(zhí)行。同樣,rcu_dereferece()原語(yǔ)確保后續(xù)指針解引用的代碼可以在Alpha CPU上看見(jiàn)對(duì)應(yīng)的rcu_assign_pointer()之前進(jìn)行的初始化的結(jié)果。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10804

    瀏覽量

    210829
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11207

    瀏覽量

    208717
  • rcu
    rcu
    +關(guān)注

    關(guān)注

    0

    文章

    21

    瀏覽量

    5435

原文標(biāo)題:謝寶友:深入理解RCU之四:用法

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    謝寶友教你學(xué)Linux深入理解Linux RCU之從硬件說(shuō)起

    RCULinux內(nèi)核中很難的一部分,本系列文章一點(diǎn)一滴地來(lái)把RCU說(shuō)清楚。第一次連載,是描述硬件。
    的頭像 發(fā)表于 09-04 10:29 ?5952次閱讀
    謝寶友教你學(xué)<b class='flag-5'>Linux</b>:<b class='flag-5'>深入理解</b><b class='flag-5'>Linux</b> <b class='flag-5'>RCU</b>之從硬件說(shuō)起

    從硬件引申出內(nèi)存屏障,帶你深入了解Linux內(nèi)核RCU

    本文從硬件的角度引申出內(nèi)存屏障,這不是內(nèi)存屏障的詳盡手冊(cè),但是相關(guān)知識(shí)對(duì)于理解RCU有所幫助。
    的頭像 發(fā)表于 09-19 11:39 ?6106次閱讀
    從硬件引申出內(nèi)存屏障,帶你<b class='flag-5'>深入</b>了解<b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>RCU</b>

    深入理解Linux RCU:經(jīng)典RCU實(shí)現(xiàn)概要

    減少競(jìng)爭(zhēng)的一個(gè)有效方法是創(chuàng)建一個(gè)分級(jí)結(jié)構(gòu),如上圖所示。在此,四個(gè)rcu_node 結(jié)構(gòu)中的每一個(gè)都有各自的,這樣只有 CPU 0 和 1 會(huì)獲取最左邊的 rcu_node的
    的頭像 發(fā)表于 05-10 09:08 ?1.5w次閱讀
    <b class='flag-5'>深入理解</b><b class='flag-5'>Linux</b> <b class='flag-5'>RCU</b>:經(jīng)典<b class='flag-5'>RCU</b>實(shí)現(xiàn)概要

    基于Linux內(nèi)核源碼的RCU實(shí)現(xiàn)方案

    RCU(Read-Copy Update)是數(shù)據(jù)同步的一種方式,在當(dāng)前的Linux內(nèi)核中發(fā)揮著重要的作用。RCU主要針對(duì)的數(shù)據(jù)對(duì)象是鏈表,目的是提高遍歷讀取數(shù)據(jù)的效率,為了達(dá)到目的使用RCU
    的頭像 發(fā)表于 09-25 15:10 ?2423次閱讀

    Linux內(nèi)核RCU的原理與使用

    好久沒(méi)有更文,上次更文時(shí)西安天氣還很熱,現(xiàn)在“寒氣”它還真來(lái)了。在前一階段經(jīng)歷了一些公司的面試,經(jīng)常會(huì)問(wèn)到RCU的原理,其實(shí)在跟對(duì)方口述表達(dá)時(shí)才真正能體現(xiàn)出來(lái)自己到底懂不懂,關(guān)于RCU
    發(fā)表于 10-13 16:17 ?4568次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核<b class='flag-5'>RCU</b><b class='flag-5'>鎖</b>的原理與使用

    深入理解RCU:玩具式實(shí)現(xiàn)

    也許最簡(jiǎn)單的RCU實(shí)現(xiàn)就是用了,如下圖所示。在該實(shí)現(xiàn)中,rcu_read_lock()獲取一把全局自旋rcu_read_unlock(
    的頭像 發(fā)表于 12-27 09:06 ?697次閱讀

    分級(jí)RCU的基礎(chǔ)知識(shí)

    雖然Linux更早版本中的經(jīng)典RCU,其讀端原語(yǔ)擁有出色的性能和擴(kuò)展性,但是寫(xiě)端原語(yǔ)則需要判斷預(yù)先存在的讀端臨界區(qū)在什么時(shí)候完成,它僅僅被設(shè)計(jì)用于數(shù)十個(gè)CPU的系統(tǒng)。經(jīng)典RCU的實(shí)現(xiàn),要求在每個(gè)優(yōu)雅
    的頭像 發(fā)表于 12-27 09:54 ?892次閱讀
    分級(jí)<b class='flag-5'>RCU</b>的基礎(chǔ)知識(shí)

    Linux內(nèi)核中RCU的用法

    Linux內(nèi)核中,RCU最常見(jiàn)的用途是替換讀寫(xiě)。在20世紀(jì)90年代初期,Paul在實(shí)現(xiàn)通用RCU之前,實(shí)現(xiàn)了一種輕量級(jí)的
    的頭像 發(fā)表于 12-27 09:56 ?1616次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核中<b class='flag-5'>RCU</b>的用法

    深入理解LINUX內(nèi)存管理》學(xué)習(xí)筆記

    深入理解LINUX內(nèi)存管理》學(xué)習(xí)筆記1
    發(fā)表于 11-07 10:20

    分級(jí)RCU基礎(chǔ)知識(shí)

    謝寶友:深入理解RCU之六:分級(jí)RCU基礎(chǔ)
    發(fā)表于 05-25 06:18

    linux經(jīng)典的rcu如何實(shí)現(xiàn)?

    RCU主要用于對(duì)性能要求苛刻的并行實(shí)時(shí)計(jì)算。例如:天氣預(yù)報(bào)、模擬核爆炸計(jì)算、內(nèi)核同步等等。
    的頭像 發(fā)表于 11-07 11:09 ?3710次閱讀
    <b class='flag-5'>linux</b>經(jīng)典的<b class='flag-5'>rcu</b>如何實(shí)現(xiàn)?

    linux內(nèi)核rcu機(jī)制詳解

    Linux內(nèi)核源碼當(dāng)中,關(guān)于RCU的文檔比較齊全,你可以在 /Documentation/RCU/ 目錄下找到這些文件。Paul E. McKenney 是內(nèi)核中RCU源碼的主要實(shí)現(xiàn)
    發(fā)表于 11-13 16:47 ?8744次閱讀
    <b class='flag-5'>linux</b>內(nèi)核<b class='flag-5'>rcu</b>機(jī)制詳解

    深入了解RCU是怎樣實(shí)現(xiàn)的?

    RCU(Read-Copy Update),顧名思義就是讀-拷貝修改,它是基于其原理命名的。對(duì)于被RCU保護(hù)的共享數(shù)據(jù)結(jié)構(gòu),讀者不需要獲得任何就可以訪問(wèn)它,但寫(xiě)在訪問(wèn)它時(shí)首先拷貝一
    發(fā)表于 05-14 17:37 ?1.4w次閱讀
     <b class='flag-5'>深入</b>了解<b class='flag-5'>RCU</b>是怎樣實(shí)現(xiàn)的?

    了解了解Linux內(nèi)核中的RCU機(jī)制

    RCU的設(shè)計(jì)思想比較明確,通過(guò)新老指針替換的方式來(lái)實(shí)現(xiàn)免方式的共享保護(hù)。但是具體到代碼的層面,理解起來(lái)多少還是會(huì)有些困難。在《深入Linux
    發(fā)表于 05-14 14:28 ?1329次閱讀

    并行程序設(shè)計(jì)中最重要的-RCU

    ,。 各個(gè)語(yǔ)言C, C++,Java, go等都有RCU實(shí)現(xiàn),同時(shí)內(nèi)核精巧的實(shí)現(xiàn)也是學(xué)習(xí)代碼設(shè)計(jì)好素材,深入理解RCU分為兩個(gè)部分,第一部分主要是講核心原理,
    的頭像 發(fā)表于 08-27 14:25 ?3095次閱讀