RCU(Read-Copy Update)的意思是读-复制更新,它是根据原理命名的。写者修改对象的过程是:首先复制生成一个副本,然后更新这个副本,最后使用新的对象替换旧的对象。在写者执行复制更新的时候读者可以读数据。
写者删除对象,必须等到所有访问被删除对象的读者访问结束,才能执行销毁操作。RCU的关键技术是怎么判断所有读者已经完成访问。等待所有读者访问结束的时间称为宽限期(grace period)。
RCU的优点是读者没有任何同步开销:不需要获取任何锁,不需要执行原子指令,(在除了阿尔法以外的处理器上)不需要执行内存屏障。但是写者的同步开销比较大,写者需要延迟对象的释放,复制被修改的对象,写者之间必须使用锁互斥。
下面介绍几个概念:
读者临界区:读者访问RCU保护的对象的代码区域。
静止状态(Quiescent State):
在RCU设计中,如果一个CPU处于RCU读者临界区中,说明它的状态是活跃的;相反,如果在时钟tick中检测到该CUP处于用户模式或者idle模式,说明该CPU已经离开了读者临界区,那么它是静止状态。在不支持抢占的RCU实现中,只要检测到CPU有进程上下文切换,就可以知道离开了读者临界区。
宽限期/优雅期(Grqce Period):
?????? GP有生命周期,有开始和结束之分。在GP开始的那一刻算起,当所有处于读者临界区的CPU都离开了临界区,也就是都至少发生了一次 Quiescent State,那么就认为一个GP可以结束了。GP结束后,RCU会调用注册的回调函数,例如销毁旧数据等。
RCU的第一个版本称为经典RCU,在内核版本2.5.43中引入,目前内核支持3种RCU。
(1)不可抢占RCU(RCU-sched)。不允许进程在读端临界区被其他进程抢占。最初的经典RCU是不可抢占RCU,后来引入了可抢占RCU,所以内核版本2.6.12为不可抢占RCU设计了一套专用编程接口。
(2)加速版不可抢占RCU(RCU-bh,bh是“bottom half”的缩写,下半部),在内核版本2.6.9中引入,是针对不可抢占RCU的改进,在软中断很多的情况下可以缩短宽限期。
内核的网络栈在软中断里面处理报文,如果攻击者疯狂地发送报文攻击设备,导致被攻击设备的处理器大部分时间执行软中断,宽限期会变得很长,大量延后执行的销毁操作没有被执行,可能导致内存耗尽。
为了应对这种分布式拒绝服务攻击,RCU-bh把执行完软中断看作处理器退出读端临界区的标志,缩短宽限期。
(3)可抢占RCU(RCU-preempt),也称为实时RCU,在内核版本2.6.26中引入。可抢占RCU允许进程在读端临界区被其他进程抢占。编译内核时需要开启配置宏CONFIG_ PREEMPT_RCU。
?不可抢占的RCU检测一个CPU是否经历过了Quiescent State:
?????? 对于rcu_sched和rcu_bh类型的RCU来说,如果在时钟tick中检测到该CUP处于用户模式或者idle模式中,说明从开始一个GP到当前时刻,当前CPU已经离开了RCU临界区,即经历过了Quiescent State。另外对于rcu_bh,如果现在没有正在处理软中断或是禁止了软中断,对于rcu_bh类型的RCU来说,也经历过了一个Quiescent State。
RCU根据数据结构可以分为以下两种。
(1)树型RCU(tree RCU):也称为基于树的分层RCU(Tree-based hierarchical RCU),为拥有几百个或几千个处理器的大型系统设计。配置宏是CONFIG_TREE_RCU。
(2)微型RCU(tiny RCU):为不需要实时响应的单处理器系统设计。配置宏是CONFIG_?TINY_RCU。