前言:
對於rcu平時寫代碼用到不是特別的多,可能是自己對rcu的理解不夠深入透徹,不能發揮其強大的特性,寫個博客學習一下,以便更深入的理解RCU的機制
rcu簡述:
RCU(Read-Copy Update),是 Linux 中比較重要的一種同步機制。更新數據的時候,需要先復制一份副本,在副本上完成修改,再一次性地替換舊數據,讀者並不需要直接與寫者進行同步,讀者與寫者也能並發的執行
原理分析
Update操作分為兩個部分:
1)Removal移除:臨界資源被多個讀者讀取,寫者在拷貝副本修改后進行更新時,第一步需要先把舊的臨界資源數據移除
2)Reclamation回收:需要把舊的數據進行回
分析角色:
Reader
1)使用rcu_read_lock
和rcu_read_unlock
來界定讀者的臨界區,訪問受RCU
保護的數據時,需要始終在該臨界區域內訪問;
2)在訪問受保護的數據之前,需要使用rcu_dereference
來獲取RCU-protected
指針;
Updater
1)多個Updater更新數據時,需要使用互斥機制進行保護;
2)Updater使用rcu_assign_pointer
來移除舊的指針指向,指向更新后的臨界資源;
Reclaimer
1)Reclaimer回收的是舊的臨界資源;
2)為了確保沒有讀者正在訪問要回收的臨界資源,Reclaimer需要等待所有的讀者退出臨界區
內核實例源碼分析:
添加元素:
1 #define list_next_rcu(list) (*((struct list_head __rcu **)(&(list)->next))) 2 3 static inline void __list_add_rcu(struct list_head *new, 4 struct list_head *prev, struct list_head *next) 5 { 6 new->next = next; 7 new->prev = prev; 8 rcu_assign_pointer(list_next_rcu(prev), new); 9 next->prev = new; 10 } 11 #define __rcu_assign_pointer(p, v, space) \ 12 ({ \ 13 smp_wmb(); \ //內存屏障 14 (p) = (typeof(*v) __force space *)(v); \ 15 })
讀取元素:
rcu_read_lock(); list_for_each_entry_rcu(pos, head, member) { // do something with `pos` } rcu_read_unlock(); list_for_each_entry_rcu->rcu_dereference->__rcu_dereference_check #define __rcu_dereference_check(p, c, space) \ ({ \ typeof(*p) *_________p1 = (typeof(*p)*__force )ACCESS_ONCE(p); \ rcu_lockdep_assert(c, "suspicious rcu_dereference_check()" \ " usage"); \ rcu_dereference_sparse(p, space); \ smp_read_barrier_depends(); \ //讀內存屏障 ((typeof(*p) __force __kernel *)(_________p1)); \ })
刪除元素
p = seach_the_entry_to_delete(); list_del_rcu(p->list); synchronize_rcu(); kfree(p); 其中 list_del_rcu() 的源碼如下,把某一項移出鏈表: /* list.h */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /* rculist.h */ static inline void list_del_rcu(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->prev = LIST_POISON2; }
更新元素
p = search_the_entry_to_update(); q = kmalloc(sizeof(*p), GFP_KERNEL); *q = *p; q->field = new_value; list_replace_rcu(&p->list, &q->list); synchronize_rcu(); kfree(p); static inline void list_replace_rcu(struct list_head *old, struct list_head *new) { new->next = old->next; new->prev = old->prev; rcu_assign_pointer(list_next_rcu(new->prev), new); new->next->prev = new; old->prev = LIST_POISON2; }
適用場景:
RCU的特性是讀者並不需要直接與寫者進行同步,讀者與寫者也能並發的執行,減少了讀的性能,對讀者性能要求高的場景非常適用
優缺點:
優點:
1)讀者側開銷很少、不需要獲取任何鎖,不需要執行原子指令或者內存屏障;
2)沒有死鎖問題;
3)沒有優先級反轉的問題;
4)沒有內存泄露的危險問題;
5)很好的實時延遲;
缺點:
1)寫者的同步開銷比較大,寫者之間需要互斥處理;
2)使用上比其他同步機制復雜;
參考文獻:
https://lwn.net/Articles/262464/
https://www.kernel.org/doc/Documentation/RCU/whatisRCU.rst
https://www.kernel.org/doc/Documentation/RCU/rcu_dereference.txt
https://www.kernel.org/doc/Documentation/RCU/rcuref.txt