一.什么是RCU机制
-
RCU是Linux中比较重要的一种同步机制.顾名思义就是 " 读,拷贝更新 " ,该机制记录了指向共享数据结构的指针的所有使用者,在该结构将要改变的时候,首先创建一个副本,在副本中修改.在所有进行读访问的使用者结束对旧副本的读取之后,指针可以替换指向新的,修改后的副本的指针,这种机制可以允许
并发读写
-
但是如果存在多个写者时,在写者把更新后的副本覆盖到原来数据之中,写者与写者需要利用其他同步机制来保证同步
-
RCU在使用的时候一般是用于读者多而写者少的情况,RCU在读者和读者之间不需要任何的系统开销,但是在写者和写者之间必须保持同步,而且写者必须要等它之间的读者全部都退出之后才能修改之前的资源
-
RCU实现的前提是
读者在访问被RCU保护的共享数据
期间是不能够被阻塞的,这是RCU机制得以实现的一个基本前提,也就是说在读者在引用被RCU保护的共享数据期间,读者所在的CPU是不能发生上下文切换的 -
RCU保护的资源必须通过指针访问
二.RCU核心API
如果指针ptr指向被RCU保护的数据结构,直接反引用指针是被禁止的,首先必须调用rcu_dereference(ptr),然后反引用返回的结果,需要使用rcu_read_lock
和rcu_read_unlock
调用来进行保护
static inline void __rcu_read_lock(void)
{
preempt_disable();
}
在内核4.1.51中是这样的,调用这个函数关闭内核的可抢占性,但是在其中是允许中断的,如果读者正在读取了一个共享数据器中的指针,发生了中断,中断处理ISR会分配一个同样大小的数据区域,之后执行回调函数,把新的数据块中的指针赋值给之前的指针,并且释放掉之前的指针
在内核可以释放内存的时候,它需要知道什么时候释放内存是安全的,RCU还提供了另外两个函数
- synchronize_rcu() 等待所有现存的读访问完成.在函数返回之后,释放与原指针相关联的内存是安全的
- call_lru可用于注册一个函数,在所有针对共享资源的读访问完成之后调用,这要求将一个rcu_head实例嵌入到RCU保护的数据结构
三.实例
内核访问RCU链表项常见的代码模式是:
rcu_read_lock();
list_for_each_entry_rcu(pos, head, member) {
// do something with `pos`
}
rcu_read_unlock();
/* `p` 指向一块受 RCU 保护的共享数据 */
/* reader */
rcu_read_lock();
p1 = rcu_dereference(p);
if (p1 != NULL) {
printk("%d\n", p1->field);
}
rcu_read_unlock();
/* free the memory */
p2 = p;
if (p2 != NULL) {
p = NULL;
synchronize_rcu();
kfree(p2);
}
多个读者可以同时处于读端的临界区域,但是一块内存数据一旦能够在读端临界区内被获取到指针引用,这块内存块数据的释放必须要等到读端临界区结束,等待读端临界区结束的内核API
是synchronize_rcu()会阻塞,直到所有的读端临界区结束之后才会返回