liburcu 提供了多种用户态 RCU 实现,这包括了:

  • QSBR

  • Memory-barrier-based RCU

  • Bullet-proof RCU

  • Signal-based RCU

QSBR 与 Linux 中的经典 RCU 非常类似,rcu_read_lock 和 rcu_read_unlock 为零开销,但是线程必须定期调用 rcu_quiescent_state 来表明自己进入了静止状态。

基于内存屏障的 RCU 和抢占式 RCU 非常类似。这种 RCU 读端开销较高,但是不需要再定期调用 rcu_quiescent_state。

Bullet-proff 和基于内存屏障的 RCU 类似,但是 RCU 读端原语会自动调用 rcu_register_thread(),这进一步增加了这些原语的开销。

基于信号的 RCU 消除了 rcu_read_lock() 和 rcu_read_unlock() 的内存屏障,但是依然允许库函数使用这些原语。这些原语的开销减小到接近 QSBR 的成都,但是这要求所有线程(包括库函数创建的)在进入第一个 RCU 读端临界区之前调用 rcu_register_thread()。

QSBR

QSBR(Quiescent-State-Based RCU) 是所有 flavor 中读性能最好的,代价是其代码侵入性较强。对于 Reader 端而言,其开销为零。

这里我们先声明几个术语:

  • 读端临界区:读端读取关键数据的地方。退出临界区后不允许再持有对旧数据的引用。读端临界区由 rcu_read_lock 和 rcu_read_unlock 所包裹。

  • 静止状态(Quiescent State):若线程不处于读端临界区,则被视为处于静止状态。

  • 宽限期:若在一个时间段内所有线程都至少进入了一次静止状态,则这个时间段被称为宽限期。

QSBR 中所有含有读端临界区的线程在启动时需要调用 rcu_register_thread,在销毁时需要调用 rcu_unregister_thread。除此之外,线程被分为上线状态和离线状态,在计算宽限期时离线状态的线程不计入。

每个线程都需要周期性地调用 rcu_quiescent_state() 来表明自己进入了 QS。一旦所有的线程在一个时间段内都调用了 rcu_quiescent_state(),这个时间段变成了一个宽限期,writer 就可以安全删除数据了。

具体而言,QSBR 中维持了一个全局计数器 rcu_gp.ctr,每当 writer 进行同步操作后都会递增该值。同时每个 reader 线程也维护了一个线程局部的计数器 rcu_reader.ctr。当 rcu_gp.ctr == rcu_reader.ctr 时证明线程已经进入了一次 QS。

在 synchronize_rcu() 中,writer 等待所有(在线的)线程局部的计数器和全局计数器一致。

Last moify: 2022-12-04 15:11:33
Build time:2025-07-18 09:41:42
Powered By asphinx