RCU学习总结

Posted mysky007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于RCU学习总结的主要内容,如果未能解决你的问题,请参考以下文章

Linux 内核 内存管理RCU 机制 ④ ( RCU 模式下更新链表项 list_replace_rcu 函数 | 链表操作时使用 smp_wmb() 函数保证代码执行顺序 )

Linux内核中的RCU机制

线程学习知识点总结

linux内核的tiny rcu, tree rcu

谢宝友: 深入理解RCU之七:分级RCU实现

Linux设备驱动程序 之 RCU机制