linux kernel 中的 rcu 機(jī)制

什么是 RCU

RCU(Read-Copy-Update)是一種用于并發(fā)訪問(wèn)共享數(shù)據(jù)結(jié)構(gòu)的機(jī)制,主要用于讀多寫(xiě)少的場(chǎng)景。RCU 機(jī)制的主要特點(diǎn)是無(wú)鎖讀(lock-free read)和延遲刪除(deferred deletion),它不需要使用顯式的互斥鎖來(lái)保護(hù)共享數(shù)據(jù)結(jié)構(gòu)的訪問(wèn),而是通過(guò)一些巧妙的手段來(lái)實(shí)現(xiàn)并發(fā)訪問(wèn)共享數(shù)據(jù)結(jié)構(gòu)的安全性和高效性。

在 RCU 機(jī)制中根穷,讀取共享數(shù)據(jù)結(jié)構(gòu)的操作是無(wú)鎖的,因此讀取操作可以并發(fā)進(jìn)行导坟,不會(huì)相互干擾缠诅。寫(xiě)入共享數(shù)據(jù)結(jié)構(gòu)的操作則使用了延遲刪除的策略,即寫(xiě)入操作并不直接修改共享數(shù)據(jù)結(jié)構(gòu)乍迄,而是將要?jiǎng)h除的數(shù)據(jù)結(jié)構(gòu)標(biāo)記為“已刪除”管引,并在之后的某個(gè)時(shí)間點(diǎn)(通常是在不會(huì)干擾讀取操作的時(shí)候)真正刪除這些數(shù)據(jù)結(jié)構(gòu)。

RCU 機(jī)制的實(shí)現(xiàn)依賴于一些底層機(jī)制闯两,比如內(nèi)存屏障褥伴、原子操作等谅将。在 Linux 內(nèi)核中,RCU 機(jī)制被廣泛應(yīng)用于多個(gè)子系統(tǒng)重慢,比如進(jìn)程管理饥臂、網(wǎng)絡(luò)協(xié)議棧等,以提高內(nèi)核的并發(fā)性能似踱。

鎖與 RCU

看起來(lái) RCU 和讀寫(xiě)鎖比較相似隅熙,當(dāng)然 kernel 中并沒(méi)有讀寫(xiě)鎖的實(shí)現(xiàn),只提供了互斥鎖和自旋鎖核芽,但 RCU 是種不同的并發(fā)編程技術(shù)囚戚,它們各自有自己的優(yōu)點(diǎn)和適用場(chǎng)景。

適合鎖的場(chǎng)景

鎖適用于短時(shí)間內(nèi)占用共享資源的情況轧简。當(dāng)一個(gè)線程獲得鎖后驰坊,其他線程需要等待該線程釋放鎖后才能獲得鎖。如果獲得鎖的線程占用時(shí)間很長(zhǎng)哮独,那么其他等待的線程會(huì)被阻塞拳芙,這樣會(huì)造成嚴(yán)重的性能問(wèn)題。因此皮璧,鎖適用于短時(shí)間內(nèi)占用共享資源的情況舟扎。

適合 RCU 的場(chǎng)景

而 RCU 則適用于讀多寫(xiě)少的情況。RCU 允許多個(gè)線程并發(fā)地讀取共享資源悴务,而不需要獲得鎖或等待其他線程釋放鎖浆竭,從而提高了并發(fā)度。當(dāng)需要修改共享資源時(shí)惨寿,RCU 會(huì)延遲對(duì)共享資源的釋放,從而避免了讀取共享資源的線程被阻塞的問(wèn)題删窒。因此裂垦,RCU 適用于讀多寫(xiě)少的情況。

在實(shí)際的系統(tǒng)開(kāi)發(fā)中肌索,自旋鎖和 RCU 可以根據(jù)實(shí)際的情況進(jìn)行選擇蕉拢,以保證系統(tǒng)的性能和正確性。

RCU 相關(guān)的 API

在 Linux 5.4.43 中诚亚,提供了以下常用的 RCU API:

  • rcu_read_lock():獲取 RCU 讀鎖晕换,讀取共享數(shù)據(jù)結(jié)構(gòu)時(shí)必須先獲取讀鎖。
  • rcu_read_unlock():釋放 RCU 讀鎖站宗。
  • synchronize_rcu():等待當(dāng)前所有使用 RCU 讀取共享數(shù)據(jù)結(jié)構(gòu)的讀者完成后再執(zhí)行后續(xù)操作闸准。
  • call_rcu():將一個(gè)回調(diào)函數(shù)關(guān)聯(lián)到 RCU 機(jī)制中,當(dāng)沒(méi)有任何 RCU 讀者在使用共享數(shù)據(jù)結(jié)構(gòu)時(shí)執(zhí)行該回調(diào)函數(shù)梢灭。
  • rcu_assign_pointer():原子地將指針賦值給共享數(shù)據(jù)結(jié)構(gòu)夷家,并確保該指針在 RCU 讀者完成讀取之前一直存在蒸其。
  • rcu_dereference():讀取共享數(shù)據(jù)結(jié)構(gòu)中的指針,返回一個(gè)指針的副本库快,并確保該指針在 RCU 讀者完成讀取之前一直存在摸袁。

這些 API 可以幫助我們使用 RCU 機(jī)制來(lái)保護(hù)共享數(shù)據(jù)結(jié)構(gòu),避免出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)和死鎖等問(wèn)題义屏。

RCU 示例

#include <linux/module.h>
#include <linux/rcupdate.h>

struct my_node {
    int val;
    struct rcu_head rcu;
    struct list_head list;
};

LIST_HEAD(my_list);

/* 添加一個(gè)節(jié)點(diǎn)到鏈表中 */
void add_node(int val)
{
    struct my_node *new_node = kmalloc(sizeof(*new_node), GFP_KERNEL);
    if (!new_node) {
        printk(KERN_ERR "Failed to allocate memory for new node\n");
        return;
    }

    new_node->val = val;
    INIT_LIST_HEAD(&new_node->list);

    /* 加入鏈表 */
    list_add(&new_node->list, &my_list);
}

/* 刪除值為 val 的節(jié)點(diǎn) */
void del_node(int val)
{
    struct my_node *node, *tmp;

    /* 遍歷鏈表并刪除匹配節(jié)點(diǎn) */
    list_for_each_entry_safe(node, tmp, &my_list, list) {
        if (node->val == val) {
            list_del_rcu(&node->list);
            call_rcu(&node->rcu, kfree);
        }
    }
}

/* 遍歷整個(gè)鏈表靠汁,打印節(jié)點(diǎn)的值 */
void traverse_list(void)
{
    struct my_node *node;

    /* 進(jìn)入 RCU 讀取臨界區(qū) */
    rcu_read_lock();

    /* 遍歷鏈表并打印節(jié)點(diǎn)的值 */
    list_for_each_entry_rcu(node, &my_list, list) {
        printk(KERN_INFO "Node value: %d\n", node->val);
    }

    /* 離開(kāi) RCU 讀取臨界區(qū) */
    rcu_read_unlock();
}

示例中,我們使用 RCU 來(lái)保護(hù)鏈表的訪問(wèn)闽铐。添加節(jié)點(diǎn)時(shí)蝶怔,我們不需要獲取鎖來(lái)保護(hù)共享資源。刪除節(jié)點(diǎn)時(shí)阳啥,我們使用了 list_del_rcu 來(lái)刪除節(jié)點(diǎn)添谊,并使用 call_rcu 函數(shù)來(lái)安排釋放內(nèi)存的回調(diào)函數(shù)。在遍歷鏈表時(shí)察迟,我們使用了 rcu_read_lock 和 rcu_read_unlock 來(lái)進(jìn)入和離開(kāi) RCU 讀取臨界區(qū)斩狱。

copy:https://github.com/cc14514/notes/blob/main/linux-kernel/rcu.md

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扎瓶,隨后出現(xiàn)的幾起案子所踊,更是在濱河造成了極大的恐慌,老刑警劉巖概荷,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秕岛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡误证,警方通過(guò)查閱死者的電腦和手機(jī)继薛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)愈捅,“玉大人遏考,你說(shuō)我怎么就攤上這事±督鳎” “怎么了灌具?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)譬巫。 經(jīng)常有香客問(wèn)我咖楣,道長(zhǎng),這世上最難降的妖魔是什么芦昔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任诱贿,我火速辦了婚禮,結(jié)果婚禮上咕缎,老公的妹妹穿的比我還像新娘瘪松。我一直安慰自己咸作,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布宵睦。 她就那樣靜靜地躺著记罚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪壳嚎。 梳的紋絲不亂的頭發(fā)上桐智,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音烟馅,去河邊找鬼说庭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛郑趁,可吹牛的內(nèi)容都是我干的刊驴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼寡润,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼捆憎!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起梭纹,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤躲惰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后变抽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體础拨,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年绍载,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诡宗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡击儡,死狀恐怖塔沃,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情曙痘,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布立肘,位于F島的核電站边坤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谅年。R本人自食惡果不足惜茧痒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望融蹂。 院中可真熱鬧旺订,春花似錦弄企、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至樱调,卻和暖如春约素,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背笆凌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工圣猎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乞而。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓送悔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親爪模。 傳聞我的和親對(duì)象是個(gè)殘疾皇子欠啤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容