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_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
以上是关于RCU学习总结的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理RCU 机制 ④ ( RCU 模式下更新链表项 list_replace_rcu 函数 | 链表操作时使用 smp_wmb() 函数保证代码执行顺序 )