原理
Read Copy Update
读(Read):读者不需要获得任何锁就可访问RCU保护的临界区;
拷贝(Copy):写者在访问临界区时,写者“自己”将先拷贝一个临界区副本,然后对副本进行修改;
更新(Update):RCU机制将在在适当时机使用一个回调函数把指向原来临界区的指针重新指向新的被修改的临界区,锁机制中的垃圾收集器负责回调函数的调用。(时机:所有引用该共享临界区的CPU都退出对临界区的操作。即没有CPU再去操作这段被RCU保护的临界区后,这段临界区即可回收了,此时回调函数即被调用)
quiescent state(静默状态过程),它表示为CPU发生上下文切换的过程
grace period(即“适当时机”),它表示为所有CPU都经历一次quiescent state所需要的等待的时间,也即系统中所有的读者完成对共享临界区的访问
RCU的结构体定义,只有一个用于串接链表的next指针和一个函数指针,这个函数指针即是上述提及的回调函数,这个需使用RCU机制的用户向链表注册,即挂接到链表下,从而在适当时机下得到调用
示例:写者从链表中删除元素B。
写者首先遍历该链表得到指向元素B的指针
然后修改元素B的前一个元素的next指针指向元素B的next指针指向的元素C,修改元素B的next指针指向的元素C的prep指针指向元素B的prep指针指向的元素A。在此期间可能有读者访问该链表,由于修改指针指向的操作是原子的,因此这个过程不需要同步,而元素B的指针并没有去修改,因为读者可能正在使用B元素来得到链表的下一个或前一个元素,即A或C。当写者完成上述操作后便向系统注册一个回调函数func以便在 grace period之后能够删除元素B,注册完毕后写着便可认为它已经完成删除操作(实际上并未完成)。
垃圾收集器在检测到所有的CPU不在引用该链表后,即所有的CPU已经经历了一次quiescent state(即grace period),当grace period完成后,系统便会去调用先前写者注册的回调函数func,从而真正的删除了元素B。这便是RCU机制的一种使用范例。
API介绍
- rcu_read_lock() & rcu_read_unlock()
#define rcu_read_lock() __rcu_read_lock()
#define rcu_read_unlock() __rcu_read_unlock()
#define __rcu_read_lock()
do {
preempt_disable();
__acquire(RCU);
rcu_read_acquire();
} while (0)
#define __rcu_read_unlock()
do {
rcu_read_release();
__release(RCU);
preempt_enable();
} while (0)
用来保持一个读者的RCU临界区.在该临界区内不允许发生上下文切换
2.rcu_dereference()
#define rcu_dereference(p) rcu_dereference_check(p, 0)
#define rcu_dereference_check(p, c)
__rcu_dereference_check((p), rcu_read_lock_held() || (c), __rcu)
#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();