编辑
2025-11-23
记录知识
0
请注意,本文编写于 168 天前,最后修改于 168 天前,其中某些信息可能已经过时。

目录

原理介绍
删除
插入
完整性
宽限期
删除操作
插入操作
总结
参考

RCU是一种基于无锁的设计, 对于多个读者来说,几乎可以等效为无锁情况,特别适合于像文件系统目录查询,DNS resolution这样的场景。
使用RCU的场景必须要满足如下两点

  • 频繁的读取,很少的写入
  • 对资源的数据没有强一致性要求

原理介绍

RCU管理的成员通常是一个内核的list或link-list。主要原理如下

删除

当对RCU的一个list的成员进行删除,这个删除不是立即删除,而是先标记为释放,等所有的读端执行所有读请求之后,再统一进行实际删除操作。 在这里,RCU将一个删除操作开始,到删除操作真的完成中间耗费的实际叫做宽限期(grace period)

image.png

插入

当对RCU的一个list的成员进行插入的时候,RCU需要保证任何时候对这个list的读取是完整的,非中间态的。也就是其他的读端,要么是未插入的状态,要么是已插入的状态,绝不能是中间状态

image.png

完整性

任何时候对list的操作,无论是删除还是插入,RCU都需要保证任何时候的读端对list的遍历都是正常的,不会出现断链问题。不过同插入一样,rcu不保证一定能够读到新增的元素,或不读到要被删除的元素。

宽限期

image.png

顾名思义,只要任务在宽限期做的任何事情,都相当于有免死金牌,中间发生的错误可以忽略。对于RCU而言,具体表达的意思就是在宽限期内的list的成员,读到旧值也是没有问题的。

换个角度,如果等待宽限期过了之后,一切需要按照原本预期发生。
所以在linux中,synchronize_rcu()执行的这段时间就是宽限期。

所以使用RCU的范式如下:

image.png

对于读端,引用一个指针,触发rcu read

rcu_read_lock(); p = rcu_dereference(gptr); if (p) { printk(KERN_INFO "Reader: val = %d\n", p->val); } rcu_read_unlock();

对于写端

old = rcu_dereference_protected(gptr, lockdep_is_held(&some_lock)); rcu_assign_pointer(gptr, new); if (old) { synchronize_rcu(); kfree(old); }

关于rcu使用的实际图示如下

image.png

当写线程要将指针 p 从指向 data1 改为指向 data2 时,需考虑读线程可能仍在使用 data1。因此,写线程会先“发布”新数据(即更新指针),然后进入等待期,直到所有读线程都退出当前的 RCU 临界区。

在此期间,不同读线程可能看到不同版本的数据:有的仍看到旧的 data1(记为 p1),有的已看到新的 data2(记为 p2)。只有当所有读线程都完成本次临界区访问后,才能确保它们全部看到 data2,此时 data1 才可安全释放。

删除操作

在linux中,通过list_del_rcu删除了一个元素,那么它整个周期的步骤如下

  1. 初始状态

假设想要删除节点5,6,7的位置

image.png

  1. 伪删除操作

为了满足RCU宽限期的要求,所有的读端可能读到两个版本,一个是删除前的版本,一个是删除后的版本,代码发生在list_del_rcu后 synchronize_rcu 前,如下

image.png

  1. 宽限期后

到宽限期后,读端只能看到一个版本,这样代码可以安全的删除节点5,6,7的位置,此时代码在 synchronize_rcu 后

image.png

插入操作

在linux中,通过list_replace_rcu替换了一个元素,那么它整个周期的步骤如下

  1. 初始状态

假设想要替换节点5,6,7的位置

image.png

  1. 伪更新

此时q元素携带值5,2,3想要替换p元素的5,6,7。伪更新操作会让代码在宽限期内看到两个版本,一个是123,567,1148。另一个是123,525,1148。对应代码为 list_replace_rcu 后, synchronize_rcu 前

image.png

  1. 宽限期后

等待宽限期完成之后,整个视角都变成一个唯一的版本,元素5,6,7可以被安全的删除了。此时代码在 执行synchronize_rcu 之后。

image.png

总结

本文简单介绍了rcu,在用户层面上为了支持高性能的开发,可以使用用户态的rcu库,例如liburcu。

rcu在内核中设计的知识比较庞大,这里只是简单将基础知识介绍一下。详细的可以参考 《深入理解RCU》 的系列文章

参考

https://gitee.com/xiebaoyou/documents/blob/master/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3RCU.zip