RCU學習總結


前言:

  對於rcu平時寫代碼用到不是特別的多,可能是自己對rcu的理解不夠深入透徹,不能發揮其強大的特性,寫個博客學習一下,以便更深入的理解RCU的機制

rcu簡述:

  RCU(Read-Copy Update),是 Linux 中比較重要的一種同步機制。更新數據的時候,需要先復制一份副本,在副本上完成修改,再一次性地替換舊數據,讀者並不需要直接與寫者進行同步,讀者與寫者也能並發的執行

原理分析

 

 

Update操作分為兩個部分:

1)Removal移除:臨界資源被多個讀者讀取,寫者在拷貝副本修改后進行更新時,第一步需要先把舊的臨界資源數據移除

2)Reclamation回收:需要把舊的數據進行回

 

分析角色: 

Reader

  1)使用rcu_read_lockrcu_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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM