# Linux 核心專題: Usperspace RCU 研究
> 執行人: yuyuan0625
> [專題解說錄影](https://youtu.be/mX2Hbv4dH0g)
## Reviewed by `Petakuo`
"具有零成本的 `rcu_read_lock()` 和 `rcu_read_unlock()`" 能否解釋這句話中零成本指的是甚麼?不占記憶體空間還是花費時間極少?又是怎麼做到的呢?
> 感謝你的疑問,零成本的用詞不夠精確,我已將敘述修改為低成本。
> 在 QSBR 中,rcu_read_lock() 和 rcu_read_unlock() 的實現是通過每個 thread 的本地變數來達成的。具體的方式如下:
> * Thread-Local Storage (TLS):每個 thread 都有自己的本地變數來追蹤它是否處於 RCU 讀臨界區域內。這些變數存儲在 Thread-Local Storage (TLS) 中。
>* 簡單標記:`rcu_read_lock()` 和 `rcu_read_unlock()` 操作僅僅是設置或清除這些本地變數。例如:
`rcu_read_lock()` 只是設置一個本地變數,表示該 thread 進入了讀臨界區域。
`rcu_read_unlock()` 則清除這個本地變數,表示該 thread 離開了讀臨界區域。
>
## Reviewed by `eleanorLYJ`
想請問五種 URCU 是如何做 memory reclamination?
我在 [Performance of memory reclamation for lockless synchronization](https://sysweb.cs.toronto.edu/publication_files/0000/0159/jpdc07.pdf) 看到 4 種 memory reclamation 策略 (quiescent-state-based reclamation (QSBR), epoch-based recla-mation (EBR), hazard-pointer-based reclamation (HPBR), and lock-free reference counting (LFRC)),想請問 5 種 urcu 中是否使用了這些策略,還是用其他策略?
> 此 5 種 flavor 都是使用 QSBR 策略來進行 memory reclamation。不同 flavor 的差異主要在於它們實現 quiescent states 和同步的方法。
## Reviewed by `stevend543`
筆記內容有提到 QSBR 需要再最後一個 `onQuiescentState` thread 做判別,請問這是如何做到的,需要額外的資料結構維護才有辦法讓所有 thread 對彼此的狀態都是 visiable?
> 每個 reader 一開始都要透過 [`urcu_qsbr_register_thread`](https://github.com/urcu/userspace-rcu/blob/d532778acc02338bedff5b1991e605036286d900/src/urcu-qsbr.c#L478) 將 reader 串連在一個全域雙向鏈結串列 registry , 藉由 registry 看每個 reader 的狀態判斷 GP 能否結束
程式碼如下:
```c
void urcu_qsbr_register_thread(void)
{
URCU_TLS(urcu_qsbr_reader).tid = pthread_self();
urcu_posix_assert(URCU_TLS(urcu_qsbr_reader).ctr == 0);
mutex_lock(&rcu_registry_lock);
urcu_posix_assert(!URCU_TLS(urcu_qsbr_reader).registered);
URCU_TLS(urcu_qsbr_reader).registered = 1;
cds_list_add(&URCU_TLS(urcu_qsbr_reader).node, ®istry);
mutex_unlock(&rcu_registry_lock);
_urcu_qsbr_thread_online();
}
```
`cds_list_add(&URCU_TLS(urcu_qsbr_reader).node, ®istry)` 是將當前 thread 的 reader 加入到全域的 `registry` 雙向鏈結串列中。
如此一來全域雙向鏈結串列 `registry` 就包含了所有已註冊的 readers。藉由檢查這個 `registry`,系統可以判斷每個 reader 的狀態,從而決定是否能夠結束 grace period (GP)。
## 任務簡介
重現 [2023 年專題: RCU 研究](https://hackmd.io/@sysprog/ry_6RHgS3) 實驗並針對近期實作予以討論。
## TODO: Userspace RCU 原理和實作考量
> 可重用 [2023 年專題: RCU 研究](https://hackmd.io/@sysprog/ry_6RHgS3) 素材,但要清楚解釋其設計和實作考量,中間你可能會遇到若干問題,記錄下來並討論。
### RCU 概念
RCU ([Read-Copy Update](https://en.wikipedia.org/wiki/Read-copy-update)) 是一種資料同步機制,在 Linux 核心扮演重要作用。在 [What is RCU, Fundamentally?](https://) 一文敘述:
> RCU achieves scalability improvements by allowing reads to occur concurrently with updates.
RCU 適用於頻繁的讀取 (即多個 readers)、但資料寫入 (即少量的 updaters/writers) 卻不頻繁的情境。例如檔案系統經常需要搜尋特定目錄,但對目錄的修改卻相對少,這就是 RCU 理想應用場景。RCU 最大化讀取操作,犧牲寫入的效能。
下圖即為 RCU 在不同更新需求場景和不同資料一致性下的適用性,圖中藍色區域即為讀多寫少且可以接受資料不一致性的場景,即為 RCU 最適合的場景。反之,紅色區域為寫入頻繁且不允許資料不一致的情形,就不適合使用 RCU。
![image](https://hackmd.io/_uploads/HJJRJt5L0.png)
> 出處: [User-space RCU](https://lwn.net/Articles/573424/#URCU)
### RCU 模型
![image](https://hackmd.io/_uploads/rksC1tq8A.png)
RCU 模型包含 3 個重要概念:
1. Removal: 在寫入端的臨界區中,進行資料讀取 (Read)、複製 (Copy) 和更改 (Update) 操作
2. Grace Period (寬限期): 一個等待時期,主要目的是確保在更新操作生效之前,所有可能使用舊數據的讀操作都已經完成。具體步驟如下:
a. 標記開始
b. 等待寬限期: 更新操作後需要等待一個寬限期,這個期間保證所有在開始點之前的讀操作都已經完成
c. 安全刪除: 在寬限期結束後,舊數據可以安全地刪除或重新利用
3. Reclamation: 回收舊資料
- QSBR (Quiescent State-Based Reclamation)
- EBR (Epoch-Based Reclamation)
### RCU 常見 APIs
#### 讀取端 (Reader)
- rcu_read_lock(): 用於標記 RCU 讀取端 [critical section](https://en.wikipedia.org/wiki/Critical_section) (以下簡稱 CS) 的開頭,可以確保目前的 reader 對於 RCU 保護的資料會有一致的讀取結果,不會受到 writer 更新的影響。
- rcu_read_unlock(): 用於標記 RCU 讀取端 CS 的結束,此指令必須和 `rcu_read_lock()` 成對
- rcu_dereference(): 用於安全 dereference 受 RCU 保護的指標。利用此 API 讀取指標時,編譯器不會對讀取操作進行重新排序,確保在 RCU 讀取端 CS 內可以正確的 dereference 指標
程式碼範例:
```c
rcu_read_lock();
struct my_struct *p = rcu_dereference(my_rcu_pointer);
// Access the RCU-protected data structure here
rcu_read_unlock();
```
#### 寫入端 (Updater)
- synchronize_rcu(): 等待所有已存在的 RCU 讀取端臨界區完成。這個操作用來確保在修改或釋放 RCU 保護的資料之前,所有的讀操作都已經完成
程式碼範例:
```c
// Update or remove an RCU-protected data structure
my_rcu_pointer = NULL;
synchronize_rcu(); // Wait for all readers to finish
// Now it is safe to free the old data structure
```
- call_rcu(): `synchronize_rcu()` 對應的非同步函式,會在所有已存在的 RCU 讀取操作完成後呼叫指定的函式
程式碼範例:
```c
struct my_struct {
int data;
struct rcu_head rcu; // RCU head, for callback
};
void my_rcu_callback(struct rcu_head *head) {
struct my_struct *p = container_of(head, struct my_struct, rcu);
kfree(p); // Free the memory
}
// Update or remove an RCU-protected data structure
struct my_struct *old_ptr = my_rcu_pointer;
my_rcu_pointer = new_ptr;
call_rcu(&old_ptr->rcu, my_rcu_callback); // Schedule the callback
```
- rcu_assign_pointer(): 用於更新受 RCU 保護的指標 (必須防止編譯器和 CPU 將此指定操作重新排序到任何初始化指向結構的指定操作之前)
程式碼範例:
```c
struct my_struct *new_ptr = kmalloc(sizeof(struct my_struct), GFP_KERNEL);
// Initialize the new structure
rcu_assign_pointer(my_rcu_pointer, new_ptr); // Safely assign the new pointer
```
- 整理表格
<table>
<tr>
<th>Category</th>
<th>Primitive</th>
<th>Purpose</th>
</tr>
<tr>
<td rowspan="3"><strong>Readers</strong></td>
<td><code>rcu_read_lock()</code></td>
<td>Start an RCU read-side critical section.</td>
</tr>
<tr>
<td><code>rcu_read_unlock()</code></td>
<td>End an RCU read-side critical section.</td>
</tr>
<tr>
<td><code>rcu_dereference()</code></td>
<td>Safely load an RCU-protected pointer.</td>
</tr>
<tr>
<td rowspan="3"><strong>Updaters</strong></td>
<td><code>synchronize_rcu()</code></td>
<td>Wait for all pre-existing RCU read-side critical sections to complete.</td>
</tr>
<tr>
<td><code>call_rcu()</code></td>
<td>Invoke the specified function after all pre-existing RCU read-side critical sections complete.</td>
</tr>
<tr>
<td><code>rcu_assign_pointer()</code></td>
<td>Safely update an RCU-protected pointer.</td>
</tr>
</table>
> 此表格擷取自 [Is Parallel Programming Hard, And, If So, What Can You Do About It?](https://mirrors.edge.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2023.06.11a.pdf)
### RCU Fundamentals
#### 1. 發布-訂閱機制 (Publish-Subscribe Mechanism)
![image](https://hackmd.io/_uploads/rJhZet5IR.png)
Updater 更新內容後,呼叫指定介面進行發布,reader 呼叫特定介面來讀取已發佈的內容。
由於現代編譯器會對程式碼進行一系列最佳化處理,而 CPU 在執行指令時也會進行重排序。儘管它們的手段不同,目標都是為了改進程式碼的執行效率。然而這些最佳化有時可能會導致非預期的結果。在 RCU 中,由於沒有 lock,因此我們需要自行處理 [memory ordering](https://en.wikipedia.org/wiki/Memory_ordering) 議題。
考慮以下程式碼:
```c=
/* Definiton of global structure */
struct foo {
int a;
int b;
int c;
};
struct foo *gp = NULL;
/* . . . */
/* =========Updater======== */
p = kmalloc(sizeof(*p), GFP_KERNEL);
p->a = 1;
p->b = 2;
p->c = 3;
gp = p;
/* =========Reader======== */
p = gp;
if (p != NULL) {
do_something_with(p->a, p->b, p->c);
}
```
我們預期程式碼的第 12 到 14 行會在第 15 行之前進行,但因為編譯器最佳化後無法保證程式碼的執行順序,因此有可能在`a`, `b`, `c` 變數的數值指派完成之前就已經指派 `gp` ,後續讀取端執行 `do_something_with(p->a, p->b, p->c)` 時造成非預期的結果。
:::danger
你如何得知上方陳述是成立的?用計算機結構的認知來解說。
注意:某些材料存在謬誤,即使是 Linux 核心內附的文件也可能出錯。
搭配 [並行程式設計: 執行順序](https://hackmd.io/@sysprog/concurrency/%2F%40sysprog%2Fconcurrency-ordering),設計實驗來解說。
TODO
:::
為了解決此問題, Linux 核心提供對 [memory barrier](https://en.wikipedia.org/wiki/Memory_barrier) 進行包裝的 `rcu_assign_pointer()` 和 `rcu_dereference()` 等巨集來確保執行順序。
於是上述程式碼的第 15 行應改為:
```c
rcu_assign_pointer(gp, p);
```
而第 18 行應改為:
```c
p = rcu_dereference(gp);
```
另外,Linux 核心也基於 `rcu_assign_pointer()` 和 `rcu_dereference()` 進行更高層次的包裝, 如 `list` 和 `hlist`。
以下表格顯示了 RCU 的發布和訂閱的 API,以及“取消發布”或撤回的附加API
| Category | Publish | Retract | Subscribe |
| :--------: | :-------- | :-------- | :--------|
| Pointers | rcu_assign_pointer() | rcu_assign_pointer(..., NULL) |rcu_dereference()|
| Lists | list_add_rcu() <br> list_add_tail_rcu() <br> list_replace_rcu()| list_del_rcu() | list_for_each_entry_rcu()|
| Hlists | hlist_add_after_rcu() <br> hlist_add_before_rcu() <br> hlist_add_head_rcu() <br> hlist_replace_rcu() | hlist_del_rcu() | hlist_for_each_entry_rcu()|
#### 2. 延遲回收機制
RCU 讀取端的 CS 以 `rcu_read_lock()` 開始,並且以對應的`rcu_read_unlock()` 結束。
RCU 使用寬限期的概念,共用資料的處理期間,寫入端執行緒發布共用資料的更新作為寬限期的起點,直到諸多讀取端執行緒得以達成取得更新資料的操作為止。 RCU 必須等待之前的讀取端都已經讀取完成(退出臨界區後)才能對已更變的資料進行回收處理。
#### 3. 多版本機制
維護最近更新物件的多個版本,適用於讀取端執行緒可接受並行地新增和刪除新物件的多個版本
### QSBR (Quiescent State-Based Reclamation) 演算法
QSBR 的關鍵概念就是決定執行緒的 quiescent state,該狀態和 CS 相對,執行緒一旦離開 CS 就會歸類在 quiescent state。每個 reader 在離開 CS 時記錄自身狀態,writer 會檢查這個狀態,當所有執行緒都都離開 CS 時,就可釋放舊節點所在的記憶體。
![image](https://hackmd.io/_uploads/r1YBsq9IA.png)
> https://doc.dpdk.org/guides/prog_guide/rcu_lib.html
識別出靜默狀態,還需要通知狀態資訊給其他執行緒,整個過程可用下圖描述:
![image](https://hackmd.io/_uploads/SJgAku28R.png)
圖中有 4 個執行緒,執行緒 `1` 執行完更新操作後,增添釋放記憶體的 `callback`,此時執行緒 `2`, `3`, `4` 讀取的都是之前的內容。需要等他們執行完成後分別呼叫 `onQuiescentState` 來表明自身已是靜默狀態,等到最後一個執行緒呼叫 onQuiescentState 時,就可呼叫之前註冊的 callback。實作過程中要考量的點就是如何選擇適合的位置執行 `onQuiescentState`,以及如何得知哪個才是最後一個執行`onQuiescentState` 的執行緒。
### URCU flavors
URCU 針對不同的應用場景提供五種不同的 RCU flavors:
1. QSBR (quiescent-state-based RCU)
2. Memory-barrier-based RCU
3. Bullet-proof RCU
4. Signal-based RCU
5. Signal-based RCU using an out-of-tree sys_membarrier() system call()
### 1. QSBR (urcu-qsbr)
相關程式碼可見 [src/urcu-qsbr.c](https://github.com/urcu/userspace-rcu/blob/master/src/urcu-qsbr.c)、[include/urcu/static/urcu-qsbr.h](https://github.com/urcu/userspace-rcu/blob/master/include/urcu/static/urcu-qsbr.h)
- 類似於“經典”的 RCU 實作:
- QSBR 與 Linux kernel 中的 RCU_TREE 和 RCU_TINY 實作類似
- 具有低成本的 `rcu_read_lock()` 和 `rcu_read_unlock()`
- 定期呼叫 `rcu_quiescent_state()`:
![image](https://hackmd.io/_uploads/S1iH-Y98C.png)
- `qsbr` flavor 在reader 結束 CS 之後並不會像其他 4 種 flavor 一樣會自動進入 quiescent state,因此 grace period 會比較長
- 每個執行緒必須定期呼叫 `rcu_quiescent_state()`,讓 reader 進入 quiescent state,類似於 kernel 中必須定期呼叫的 `schedule()`
- 執行緒註冊和取消註冊:
- 每個執行 RCU 讀取端臨界區的執行緒在創建後必須呼叫 `rcu_register_thread()`
- 執行緒在退出前必須呼叫 `rcu_unregister_thread()`
- 上述的要求對整體應用施加了嚴格的限制,使得 QSBR RCU 無法在大多函式庫中使用
- 性能優勢:
- QSBR 提供優秀的性能
:::danger
除了「搬運」既有的素材,難道你不會好奇,QSBR 何以有更好的性能?又,既然有 QSBR,為何要探討 EBR 呢?
:::
### 2. Memory-barrier-based RCU (qsbr-mb)
相關程式碼可見 [src/urcu.c](https://github.com/urcu/userspace-rcu/blob/master/src/urcu.c)、[include/urcu/static/urcu-mb.h](https://github.com/urcu/userspace-rcu/blob/master/include/urcu/static/urcu-mb.h)
- 類似於早期的可搶佔 RCU 實現:
- 類似於最早被接受到 `-rt patchset` 中的可搶佔 RCU 實作
- `rcu_read_lock()` 和 `rcu_read_unlock()` 包含 memory barrier (但不包含重量級的 atomics 指令)
- 讀取端成本較高
- 由於包含 memory barrier,此實作相較於標準 RCU 有更高的讀取成本
- 不需要定期呼叫
- 不像 QSBR 需要定期呼叫 `rcu_quiescent_state()`,因此能用於函式庫中
- 實作上 `mb`、`signal`、`MEMBARRIER` 類似,後面兩者即是對讀取端的 memory barrier 進行改善
### 3. Bullet-proof RCU (qsbr-bp)
相關程式碼可見 [src/urcu.c](https://github.com/urcu/userspace-rcu/blob/master/src/urcu.c)、[include/urcu/static/urcu-bp.h](https://github.com/urcu/userspace-rcu/blob/master/include/urcu/static/urcu-bp.h)
- 自動呼叫 `rcu_register_thread()` / `rcu_unregister_thread()`
- 讀取端在進入臨界區前會自動 register,讀取端執行緒結束前會自動 unregister
- RCU primitives 的成本提高 (讀、寫都較慢)
- 由於需要自動呼叫 `rcu_register_thread()`,進一步增加了成本
- 當使用者無法掌控執行緒的開始和結束時, bp flavor 會是最適合的選擇
<!-- #### 實作 (待補) -->
### 4. Signal-based RCU (qsbr-signal)
- 從 `rcu_read_lock()` 和 `rcu_read_unlock()` 中移除 memory barriers,因此擁有最接近 qsbr flavor 的讀取端效能
- 需要放棄一個 POSIX 訊號,該訊號將被 `synchronize_rcu()` 用於取代讀取端的 memory barriers
- 即使移除了 memory barriers,Signal-based RCU 仍允許 primitives 被函式庫使用
- 目前此實作因為訊號產生的副作用已在 2023 年被棄用
> [Phase 1 of deprecating liburcu-signal](https://lists.lttng.org/pipermail/lttng-dev/2023-May/030444.html)
> [Complete removal of urcu-signal flavor](https://github.com/urcu/userspace-rcu/commit/aad674a9a583e09e854145f18c5d8854269dce8c)
簡單來說,Signal-based RCU 減少了primitives 的成本,但需要使用者放棄一個 POSIX 訊號,並且所有執行緒在使用 RCU 之前必須註冊。
### 5. Signal-based RCU using an out-of-tree `sys_membarrier()` system call (qsbr-memb)
相關程式碼可見 [`src/urcu.c`](https://github.com/urcu/userspace-rcu/blob/master/src/urcu.c)、[`include/urcu/static/urcu-memb.h`](https://github.com/urcu/userspace-rcu/blob/master/include/urcu/static/urcu-memb.h)
- 以實驗性的 `sys_membarrier()` 系統呼叫取代 Signal-based RCU 使用的 POSIX 訊號,而 `sys_membarrier()` 因為可以只通知正在執行的執行緒,所以相較於 POSIX 訊號更輕量化
- 減少中斷範圍
- 與 signal flavor 對註冊為 RCU 讀取端的每個執行緒發送 POSIX 訊號不同,memb flavor 只中斷當前正在運行的執行緒
- Blocking 的執行緒在過程中已經執行過 memory barriers,並且在喚醒時會再次執行,所以不需要中斷,使得整體性能更高
簡單來說,memb flavor 透過使用處理器間中斷而非 POSIX 信號減少對系統的負擔,同時只中斷當前運行的執行緒來進一步優化性能。
#### 實作
* [`rcu_init`](https://github.com/urcu/userspace-rcu/blob/1a0ddffb9027fa82f360fdd1eb06d27d0942cc8c/src/urcu.c#L652) :
* [`rcu_sys_membarrier_init`](https://github.com/urcu/userspace-rcu/blob/1a0ddffb9027fa82f360fdd1eb06d27d0942cc8c/src/urcu.c#L633) : 檢查作業系統核心是否支援 `sys_membarrier` ,沒有則退回 mb flavor
* [`smp_mb_master`](https://github.com/urcu/userspace-rcu/blob/1a0ddffb9027fa82f360fdd1eb06d27d0942cc8c/src/urcu.c#L182) : 若 kernel 支援 `sys_membarrier`, writer 透過它將 reader 的 compiler barrier 提升成 memory barrier
* [`urcu_memb_smp_mb_slave`](https://github.com/urcu/userspace-rcu/blob/1a0ddffb9027fa82f360fdd1eb06d27d0942cc8c/include/urcu/static/urcu-memb.h#L75) : 若 kernel 支援 `sys_membarrier`, 將 memory barrier 換成 compiler barrier
### 各種 RCU flavor 的應用場景
:::danger
除了「搬運」,你能說出個別 RCU flavor 對應到哪些真實世界的應用場景嗎?
:::
- `qsbr` 版本: 若使用者非常在意效能且能清楚知道讀取端的每個執行緒如何進入 quiescent state 以避免寫入端被 block 太久或寬限期無法結束。
- 反之則適合使用另外三種 flavor,使用者就可以不用考慮何時讓 reader 進入 quiescent state
- `mb` 版本: 適合於無法讓出 POSIX 信號或不支援 membarrier 且需要最佳寫入端效能的情況
- `signal` 版本: 適合於可以讓出 POSIX 信號且最在意讀取端效能的情況
- `memb` 版本: 適合於支援 membarrier 並希望平衡讀取端和寫入端效能的情況
:::danger
還是沒解釋「真實世界」中的應用,例如 Linux 核心和 LTT-ng 怎麼運用?
不要刪除這些標注的訊息,除非你獲得充分的檢閱。
:::
- 小結
![image](https://hackmd.io/_uploads/r1r_gt9UC.png)
> 本表格擷取自 [User-space RCU](https://lwn.net/Articles/573424/#URCU#When%20is%20URCU%20Useful?)
## TODO: 重現 2023 年實驗
> 比較不同 flavor 的讀寫效能,解讀其表現。注意:近期程式碼可能會有不同的表現,而且你可能會遇到某些程式碼無法運作,試圖修正,並貢獻回 Userspace RCU 專案。
### [Userspace RCU](https://github.com/urcu/userspace-rcu/tree/master) 安裝
#### Building
```shell
./bootstrap # skip if using tarball
./configure
make
make install
ldconfig
```
其中 `./bootstrap` 需要使用 `Autoconf` 和 `libtool`
另外雖然官方教學沒有寫,但`make install` 和 `ldconfig` 都需要利用 `sudo` 提高權限才能成功執行。
```
sudo apt-get install autoconf
sudo apt-get install libtool
```
#### Benchmark 實驗
使用 [tests/benchmark](https://github.com/urcu/userspace-rcu/tree/master/tests/benchmark) 目錄下的測試檔來比較以上 4 種 (signal flavor 已棄用,故不討論)flavor 的讀寫效能。
```shell
./test_urcu_qsbr 6 2 1 /*6 writers, 2 readers, 1 second*/
```
下表為 6 reader, 2 writer, 1 second 的結果
|6 reader/ 2 writer | read | write |
|--|--|--|
|qsbr|1735352485|235117|
|signal|-|-|
|memb|138286044|754509|
|mb|136154230|721500|
|bp|673107223|122460|
可以觀察到 bp flavor 的 read 次數比 memb 高, 推測是因為 bp flavor 的 write 次數較少,因此產生的 cache-line exchanges 也較少,所以 reader 受到 cache 的效能影響降低。
至於 bp flavor write 次數較少的原因是因為在 `urcu_bp_synchronize_rcu` 要將 signal block ,來確保 signal safe,透過 perf 可以發現它佔整體的 90%,產生的 overhead 非常高。
:::danger
解釋其設計考量,要能充分對應到你的實驗結果。
:::
```
$ sudo perf record -g --call-graph dwarf ./test_urcu_bp 0 1 1
$ sudo perf report --stdio -g graph,0.5,caller
|--94.58%--urcu_bp_synchronize_rcu
| |
| |--90.80%--__GI___pthread_sigmask (inlined)
| | |
| | |--66.20%--entry_SYSCALL_64_after_hwframe
| | | |
| | | |--45.75%--do_syscall_64
| | | | |
| | | | |--32.16%--syscall_exit_to_user_mode
| | | | | |
| | | | | --1.91%--exit_to_user_mode_prepare
| | | | |
```
## 參考資料
- [Linux 核心專題: RCU 研究](https://hackmd.io/@sysprog/ry_6RHgS3)
- [RCU concepts](https://www.kernel.org/doc/html/v6.6-rc1/RCU/index.html)
- [What is RCU? -- "Read, Copy, Update"](https://www.kernel.org/doc/html/v6.6-rc1/RCU/whatisRCU.html#rcu-read-lock)
- [Is Parallel Programming Hard, And, If So, What Can You Do About It?](https://mirrors.edge.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.2023.06.11a.pdf)
<s>
- [Linux RCU 原理剖析 (一) 初窺門徑](https://www.cnblogs.com/LoyenWang/p/12681494.html)
</s>
:::danger
以第一手的材料為主,特別是 Paul E. McKenney 博士撰寫的論文和技術報告。
> 好的,因為此網頁有出現在[Linux 核心設計: RCU 同步機制](https://hackmd.io/@sysprog/linux-rcu#%E8%B3%87%E6%96%99%E8%AE%80%E5%8F%96%E7%9A%84%E5%AE%8C%E6%95%B4%E6%80%A7)的延伸閱讀中,所以學生才會也納入參考。會再多參考 Paul E. McKenney 博士的第一手材料!
> - [User-Level Implementations of Read-Copy Update](https://www.efficios.com/pub/rcu/urcu-main.pdf)
> - [Supplementary Material for User-Level Implementations of Read-Copy Update](https://www.researchgate.net/publication/228865533_Supplementary_Material_for_User-Level_Implementations_of_Read-Copy_Update)
:::
- [URCU flavors](https://lwn.net/Articles/573424/#URCU%20Flavors)
- [Usage of liburcu-memb](https://github.com/urcu/userspace-rcu/tree/master?tab=readme-ov-file#usage-of-liburcu-memb)
## 針對其他學員期末專題發表提出的問題或建議
- [Linux 核心專題: workqueue 研究](https://hackmd.io/16KnSCAFRCSH6r7WQ_3XWg)
- [Linux 核心專題: 並行佇列設計和實作](https://hackmd.io/sevSO8DbQXW5s8LIB2mX6g)
- [Linux 核心專題: 重作第 7 週測驗題](https://hackmd.io/GHEQSBRHQ3ijvetbrrMdHw)
- [Linux 核心專題: RCU 實作](https://hackmd.io/@sysprog/HkkWZ20B0)
- [Linux 核心專題:位元運算的應用](https://hackmd.io/QGMPoEQZQy2Y2RW-uimBHw)