最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

一文深入分析|RCU原理

2023-03-14 15:59 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

Linux 內(nèi)核設(shè)計了多種鎖機(jī)制,比如?讀寫鎖自旋鎖?和?信號量?等。為什么要設(shè)計這么多鎖機(jī)制呢?這是因為不同的鎖機(jī)制適用于不同的場景,比如?讀寫鎖?適用于讀多寫少的場景;而?信號量?適用于進(jìn)程長時間占用鎖,并且允許上下文切換的場景。

本文主要介紹一種 Linux 內(nèi)核中性能非常高的鎖機(jī)制:RCU鎖機(jī)制。

RCU?是?Read Copy Update?的縮寫,中文意思是?讀取、復(fù)制、更新。RCU鎖機(jī)制 就是通過讀取、復(fù)制和更新這三個操作來實現(xiàn)鎖功能。在介紹?RCU鎖?之前,我們先來看看下面的實例。

假如有線程 A 和線程 B 同時執(zhí)行?foo_read(),而另線程 C 執(zhí)行?foo_update(),那么會出現(xiàn)以下幾種情況:

  1. 線程 A 和線程 B 同時讀取到舊的 gbl_foo 的指針。

  2. 線程 A 和線程 B 同時讀取到新的 gbl_foo 的指針。

  3. 線程 A 和線程 B 有一個讀取到新的 gbl_foo 的指針,另外一個讀取到舊的 gbl_foo 的指針。

如果線程 A 或線程 B 在讀取舊的 gbl_foo 數(shù)據(jù)還沒完成時,線程 C 釋放了舊的 gbl_foo 指針,那么將會導(dǎo)致程序奔潰。

也就是說,在不加鎖的情況下,對公共數(shù)據(jù)的訪問是危險的。當(dāng)然,我們可以使用?讀寫鎖、信號量?或者?自旋鎖?來對公共數(shù)據(jù)進(jìn)行保護(hù)。但這些鎖都有各自的弊端,比如:

  • 讀寫鎖:對于寫操作較多的場景,性能會非常差。

  • 信號量:上鎖失敗的進(jìn)程將會切換上下文,從而導(dǎo)致系統(tǒng)的性能下降。

  • 自旋鎖:獲得鎖的 CPU 將會阻塞其他 CPU 的允許,從而導(dǎo)致系統(tǒng)的并行能力下降。

那么有沒有一種鎖機(jī)制,對系統(tǒng)的性能影響不大的呢?所以,Linux 內(nèi)核黑客們就創(chuàng)造出?RCU鎖

RCU鎖原理

如果能夠保證所有使用某個公共數(shù)據(jù)的線程不再使用它,那么就可以安全刪除此公共數(shù)據(jù)。

1. 寬限期

在上面的例子中,如果能夠保證線程 A 和線程 B 不再使用舊數(shù)據(jù),那么線程 C 就能安全刪除舊數(shù)據(jù)。

如下圖所示(舊數(shù)據(jù)對應(yīng)對象A,新數(shù)據(jù)對應(yīng)對象B):


從上圖的時間線可以看出,線程 A 和線程 B 從 glb_foo 指針獲取的都是對象 A 的引用。

提示:因為 glb_foo 指針在時間點 B 才被替換成對象 B,而線程 A 和線程 B 都是在時間點 B 前獲取 glb_foo 指針指向的對象,所以它們獲取到的都是對象 A 的引用。

而在?安全點?后,線程 A 和線程 B 便不再使用舊數(shù)據(jù)(對象A)。所以此時,線程 C 便可以安全釋放舊數(shù)據(jù)(對象A)。

線程 A 和線程 B 使用舊數(shù)據(jù)的這段期間,被稱為?寬限期。如下圖所示:


所以,RCU鎖?的核心思想就是怎么確定?寬限期。因為確定寬限期后,就可以隨心所欲地釋放舊數(shù)據(jù)。

2. 寬限期確認(rèn)

RCU鎖?的原理雖然比較簡單,但是實現(xiàn)卻有點小復(fù)雜,主要是因為?寬限期?的確定比較麻煩。

為了能夠確認(rèn)?寬限期,使用 RCU 鎖時有以下限制:

  • 使用 RCU 鎖前,必須禁止內(nèi)核搶占。

  • 在 RCU 鎖保護(hù)的臨界區(qū)中,不能使用可能觸發(fā)調(diào)度的函數(shù)(如不能調(diào)用 alloc_pages 函數(shù))。

由于在 RCU 臨界區(qū)是禁止調(diào)度的,所以如果 CPU 發(fā)生了調(diào)度,就可以確定當(dāng)前線程已經(jīng)退出了臨界區(qū)(也就是說當(dāng)前線程不再引用舊對象)。如果所有的 CPU 都至少發(fā)生過一次調(diào)度,那么也就說明沒有任何線程引用舊對象,此時就可以安全釋放舊對象了。

所以,RCU 鎖的核心原理是:在釋放舊對象前,必須等待所有 CPU 核心至少調(diào)度一次。如下代碼所示:

foo_update()?函數(shù)釋放舊對象的步驟如下:

  1. 使用新對象替換舊對象,在替換前必須使用自旋鎖進(jìn)行保護(hù),避免多個 CPU 同時修改 gbl_foo 指針的值。

  2. 等待所有 CPU 核心至少調(diào)度一次。

  3. 由于所有 CPU 核心都至少調(diào)度過一次,那么可以確認(rèn)現(xiàn)在沒有線程引用舊對象,所以可以安全釋放舊對象。

【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ? ??


3. RCU臨界區(qū)

通過前面的分析可知,在 RCU 臨界區(qū)中是不能發(fā)生調(diào)度的。要保證臨界區(qū)不發(fā)生調(diào)度,首先要確保在臨界區(qū)中不能調(diào)用可能觸發(fā)調(diào)度的函數(shù),如:alloc_pages()。這點需要 RCU 使用者自己保證。

另外一點要保證的是,內(nèi)核不能發(fā)生搶占,這點可以通過調(diào)用?preempt_disable()?函數(shù)實現(xiàn)。內(nèi)核定義了一個名為?rcu_read_lock()?的宏,如下所示:

可以看出,?rcu_read_lock()?宏其實就是?preempt_disable()?函數(shù)的別名。所以,使用 RCU 鎖時,可以使用?rcu_read_lock()?宏對臨界區(qū)進(jìn)行保護(hù)。

當(dāng)退出臨界區(qū)時,需要調(diào)用?rcu_read_unlock()?把內(nèi)核搶占打開。rcu_read_unlock()?的定義如下:

可以看出,rcu_read_unlock()?宏就是?preempt_enable()?的別名。

所以,當(dāng)我們使用 RCU 鎖對臨界區(qū)進(jìn)行保護(hù)時,必須將需要保護(hù)的代碼放置在?rcu_read_lock()?和?rcu_read_unlock()?之間,如下所示:


原文作者:Linux內(nèi)核那些事



一文深入分析|RCU原理的評論 (共 條)

分享到微博請遵守國家法律
黄骅市| 九台市| 雷州市| 巩留县| 巧家县| 土默特左旗| 龙岩市| 美姑县| 紫云| 秦安县| 文化| 孝义市| 陇川县| 长沙县| 钟山县| 阳曲县| 徐水县| 揭西县| 平潭县| 宜君县| 宁波市| 白银市| 岳阳县| 夹江县| 运城市| 同心县| 安达市| 安阳县| 鹤庆县| 石泉县| 连云港市| 玉山县| 芦溪县| 葵青区| 吉林省| 铁力市| 石屏县| 玉环县| 丘北县| 龙南县| 邵阳县|