读取-复制-更新(read-copy-update,RCU)是一种高级的互斥机制,在正确的条件下,可以获得高的性能;
RCU对它保护的数据结构做了一些限定,它针对经常发生读而很少发生写的情况做了优化,被保护的资源应该通过指针访问,而对这些资源的引用必须仅由原子代码拥有,在RCU保护的代码范围内不能进入睡眠状态;在修改该数据结构时,写入线程首先复制,然后修改副本,之后用新的版本替换相关指针,当内核确信老的版本上没有其他引用时,就可以释放老的版本了;
使用RCU的代码应该包含<linux/rcupdate.h>头文件;
RCU读操作的使用方法如下,注意反引用的指针不能在RCU锁之外使用;
1 rcu_read_lock(); 2 3 p = rcu_dereference(ptr); 4 if (p != NULL) { 5 /* 读取操作 */ 6 } 7 8 rcu_read_unlock();
RCU写操作的使用方法如下,首先需要分配一个新的结构,如果必要则从旧的结构中复制数据,然后将读取代码能看到的指针替换掉;
1 struct some_struct * new_ptr = kmalloc(...); 2 3 new_prt->xxx = xx; 4 5 rcu_assign_pointer(ptr, new_ptr);
在写端完成之后,剩下的工作就是释放旧的数据结构;当然,这时其他处理器上运行的代码可能扔在引用旧的数据,因此不能立即释放旧的结构;写入代码必须等待直到能够确信不存在这样的引用;
因为拥有对该数据结构的引用的代码都必须是原子的,因此我们可以知道,一旦系统中的每个处理器都至少调用一次后,所有的引用都会消失;因此,RCU所做的就是,设置一个回调函数并等待所有的处理器被调度,之后由回调函数完成清理工作;
RCU提供了两个函数分别为同步等待所有读取结束和异步等待读取结束后释放旧结构资源;
同步:synchronize_rcu(),修改之后调用该函数等待所有读引引用结束,在函数返回之后进行旧资源的释放;
1 void synchronize_rcu(void);
异步:call_rcu(),通过该函数注册一个释放旧资源的回调函数,在所有的读访问完成之后,回调注册函数,进行旧资源的释放;
1 void call_rcu(struct rcu_head *head, rcu_callback_t func);