iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
自我挑戰組

當你凝視linux, linux也在凝視你系列 第 25

Day25 RCU 同步機制

  • 分享至 

  • xImage
  •  

前言

前幾天介紹了 mutex, semaphore, spinlock, read-write lock, 這些鎖有著各式各樣的功能,為什麼還要單獨設計一種新機制取代現有的這些鎖,甚至這個新機制的運作比過往的鎖都還要複雜,讓我們繼續看下去

RCU(Read-Copy Update, RCU)

相比於過去的同步機制,包含 mutex, semaphore, spinlock 等,這些已知的鎖都是使用了最小操作(atomic operation),因此在多CPU爭搶共享變數時會讓緩衝記憶體的一致性變得很糟糕,造成 cache bouncing,讓整體效能降低。因此RCU 想要做到的事情是讓讀取的執行序再也沒有為了同步所產生的效能損耗,或者讓損耗變得很小,甚至可以忽略不計,也不用有額外的鎖,不需要 atomic operation和記憶體屏障,就可以順利的訪問資料。
RCU 適用於頻繁的讀取 (即多個 reader)、但資料寫入 (即少量的 updater/writer) 卻不頻繁的情境,並且對資料的一致性沒有強烈的要求,像是如果有多個執行緒同時要讀取某個鏈表上的節點,如果有執行緒對該節點寫入,可以接受有些執行緒讀取到新的值,有些執行緒讀取到舊的值。
以下這張圖是比較 rw-lock 與 RCU 機制在資料被更新時的差異,綠色代表開始讀取到updater更新過的新值。
上半部的圖是 rw-lock 進行同步,可以看見當收到更新資料的請求時, writer 必須要等到三個reader都停止讀取之後才能夠寫入,在那之前writer 必須要spin;在writer更新的時候,reader也沒辦法做其他事情,只能spin等待寫入的完成。
下半部的圖是使用 RCU 進行同步,可以發現收到update 的虛請求之後,RCU updater馬上開始運轉,reader也是能夠持續讀取,直到 updater完成之後,新加入的reader就可以讀取到新的值了。

以下用一個鏈表作為例子,分為四個步驟

  1. 初始狀態
  2. 收到update請求,開始進行更新
  3. update完成,新reader 讀取新資料
  4. 就資料的reader皆已結束,回收舊node。

以下列出幾個RCU 常用的API :
rcu_read_lock()/rcu_read_unlock() : 組成一個read的RCU C.S。
rcu_dereference(): 獲取被RCU保護的指針,reader 執行緒為了訪問RCU保護的共享資料,需要使用此函數創立一個新的指針,並且指向RCU保護的資料。
rcu_assign_pointer() :用於寫入執行緒,在寫入新數據後,使用此函數可以讓RCU保護的指針指向新建立的函數,換句話說就是發布更新後的資料,如同上圖的步驟三。
synchronize_rcu() : 等待所有現存的讀取訪問完成,準備將舊資料刪除。
call_rcu() :註冊一個callback函數,當線存的讀取訪問完成後,使用這個函數銷毀數據。

在Linux kernel中, <Documents/RCU>裡面有許多跟RCU有關的資料,如果之後有時間,我會去多加研究。

Linux 核心設計: RCU 同步機制

ref


上一篇
Day24 read-write lock
下一篇
Day26 interrupt, exception
系列文
當你凝視linux, linux也在凝視你30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言