RCU锁基本概念

设计动机

在CPU核数很多的情况下,核间同步需要的硬件代价越来越大。频繁地数据同步会限制多核的性能,因为核间同步必然涉及所有CPU核。提高每个CPU核的使用率,比降低CPU执行延时更能发挥多核优势。全局锁比如spinlock的实现涉及核间同步,多核情况下会愈发耗性能。可以针对特定的场景设计性能损耗更低的并发控制机制。RCU是针对读多写少设计的进程数据同步机制。RCU的读者不加锁,只有写者加锁,性能比锁机制更高。

临界资源访问

访问原则有两个:

  1. 读者访问临界资源时不加锁,只标记自己进入了临界资源区。
  2. 写者如果要更新临界资源,首先拷贝一份要更新的数据,在拷贝上进行数据修改,修改完之后,将更新的数据作为后面进入临界区的读者要访问的数据,然后检查临界区是否有其它读者正在访问资源,如果有,等到所有读者资源访问结束,再删除旧的数据

写者等待所有读者资源访问结束的这段时间,叫做宽限期。RCU访问原则的最终目的有两个:

  1. 在写者更新临界区前已经访问临界区的读者,看到的临界区资源在整个更新过程中不会变化
  2. 在写者更新完后访问临界区的读者,看到的临界区资源是更新后的
    RCU允许宽限期内的读者看到的临界区资源不一致。

基本概念

  • 静默态:一种描述临界区资源的状态,在这种状态下没有用户进程访问临界区资源。
  • 宽限期:写者等待所有读者资源访问结束的这段时间。宽限期也是一个更通用的术语,表示当写者经过宽限期之后,它修改的临界区资源不再有读者访问。

实现演化

使用全局位图

在这里插入图片描述
原理:每个cpu维护一个per-cpu计数器,计数器维护锁计数,一旦这个cpu进入静默态(计数器变成0),在全局的位图相应位清零。当全局位图都是0,表示宽限期结束。
优点:设计简单
缺点:

  1. 读操作慢,每个cpu进入静默态时都要去更新全局的位图
  2. 只有在进入静默态,cpu才去更新全局,如果cpu在统计的时间段内一直处于静默,全局位图不会更新。
  3. 更新全局位图的需要加锁,保证多核并发操作时的数据一致,这个违背了写者不加锁的原则。降低了性能。

强制进入静默态

在这里插入图片描述
在这里插入图片描述
原理:使用一个daemon来实现宽限期,当写者要求更新临界资源时,daemon主动抢占每个cpu,让每个cpu都执行一次daemon程序,目的是使cpu上下文发生切换,让cpu进入静默态。当每个cpu都执行了一次daemon程序,就可以宣称宽限期结束。
优点:设计简单
缺点:写者更新的效率比较低,需要维护一个daemon

使用全局位图的拷贝

在这里插入图片描述
原理:维护一个全局的位图,每当有cpu进入静默态,就拷贝一份全局的位图,在对应bit上清零
优点:相比使用全局位图,用拷贝的话不需要加锁,相比于第一种用锁实现数据同步,这里用MESI实现数据同步。
缺点:频繁访问全局位图代价比较大,CPU要频繁使用MESI协议保证所有核上的cache数据的一致性,同样涉及硬件。

经典的RCU实现

上面的实现效率低,原因有两个:

  1. cpu静默态是主动发起的,这总需要额外的工作,但kernel其实一直有统计信息可以表明静默态的特征。当kernel发生系统调用,陷入,上下文切换时,都是静默态的临界状态。同时cpu空闲和暂停,也间接表示了静默态的临界状态,这些状态中,只要一个状态向另外一个状态转换,就表明cpu一定是进入了静默态。所以只需要统计这些状态的计数,对比当前计数和一段时间前的计数,就可以知道这段时间内cpu是否有进入静默态。
  2. 在统计进入静默态的cpu时,需要保证多核看到的数据一致,要么就加锁,要么就拷贝。经典的实现是把静默态的统计放到per-cpu上,由于per-cpu变量是cpu的私有变量,只有本地cpu可以修改,所以不存在数据同步。

因此经典RCU算法实现如下:

  1. 等待宽限期的写者,注册一个callback到per-cpu的链表
  2. 在一个合适的时间点后,通知所有CPU,现在开始统计宽限期
  3. 当每个CPU收到通知后,知道自己要进入新的宽限期后,将自己的静默态统计次数的快照保存下来
  4. 每个CPU周期性的对比当前静默态次数和快照的值,如果相同表示没有进入静默态,如果不同,表示进入了静默态,记录下来。当最后一个CPU进入静默态后,表明宽限期结束
  5. 每个CPU都有手段知道宽限期的结束,所以可以任意触发一个CPU上的callback,用于通知写者

每个CPU上都注册一个callback,虽然浪费了内存,但是cpu不会有同步信息的操作,因此节约了cpu的成本,相当于用内存换CPU。
算法具体实现中还要考虑以下问题:

  1. 写者注册callback的时机是随即的,当一个CPU正处在一个宽限期的统计中,又来了一个callback,要求统计一个宽限期。这种情况需要考虑。
    解决方法:每个CPU不止维护一个当前宽限期的callback(curlist),还维护下一个宽限期的callback(nextlist),每个宽限期通过一个id(generation number)区别。当前CPU可能统计的是当前宽限期,但另一个CPU已经进入静默态,就可以统计下一个宽限期,不同CPU可能统计的宽限期不同,可以通过id区分,但不管怎样,所有CPU,只能统计最多两个宽限期。
  2. 宽限期的统计开始通知和结束通知,需要知会到每个cpu,需要高效
    解决方法:对每个CPU的静默态次数检查,放在了软中断中,当CPU进入静默态,在软中断上下文触发callback。

SRCU锁 - Sleepable RCU

  • 正常情况下,如果在临界区睡眠,相当于CPU进入了静默态,但RCU宽限期统计中不允许有静默态出现在读者访问的临界区里面,否则会影响宽限器的检测,最后造成宽限期无限延长。由于宽限期无限延长,对同一个CPU上的写者来说,注册宽限期结束时的callback是异步的,放到了软中断中,因此一个CPU上的一个进程就可以多次注册宽限期结束的callback,如此迭代最后会造成系统内存资源耗尽然后崩溃。因此RCU规定读者在访问临界区资源不允许睡眠。
  • 但在实时系统中,必须实现高优先级的进程抢占低优先级的进程,如果要考虑读者进程访问临界区时CPU被抢占,不得不睡眠的情况。因此提出了可睡眠的RCU。
  • SRCU不提倡在临界区睡眠,只考虑如果在临界区睡眠了,把其影响降到最低。通过两个手段来降低睡眠的影响:
  1. SRCU只提供宽限期检测的同步接口,当一个CPU上进程的写者发起宽限期检查后,只能同步地等待这个宽限期结束。这样保证了一个CPU上一个进程只能注册一个callback。
  2. 将宽限期检查和统计限制在一个子系统中而非整个系统上的进程。这样保证睡眠只影响这个子系统中的写者进程。
  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Android 中,RCU(Read-Copy Update)是一种用于实现读写并发的同步机制。它主要用于多核系统中,通过提供一种读者之间不需要互斥访问共享数据的机制,从而提高并发性能。 RCU 在 Android 中的实现基于 Linux 内核的 RCU 机制,并在其上进行了一些优化和扩展。Android 中的 RCU 主要用于对共享数据进行读操作,以提高性能并减少竞争。 Android 中的 RCU 机制包括以下几个重要组件和概念: 1. `rcu_read_lock()` 和 `rcu_read_unlock()`:这是 RCU 读取的接口函数。通过在读取共享数据之前调用 `rcu_read_lock()`,并在读取完成后调用 `rcu_read_unlock()`,可以告知系统当前线程正在进行 RCU 读取操作。 2. `rcu_dereference()`:这是一个宏,用于访问 RCU 保护的共享数据。它确保在访问共享数据期间,不会发生数据被修改或释放的情况。通过 `rcu_dereference()` 宏进行访问,可以避免显式加和解的开销。 3. `synchronize_rcu()`:这是一个同步函数,用于等待当前正在进行的 RCU 读操作完成。当需要修改共享数据时,可以调用 `synchronize_rcu()` 来等待所有之前的 RCU 读取操作完成,以确保数据的一致性。 4. `call_rcu()`:这是一个用于延迟释放共享数据的函数。当共享数据不再需要时,可以使用 `call_rcu()` 注册一个回调函数,在所有正在进行的 RCU 读取操作完成后,异步释放共享数据。 通过使用 RCU,Android 在某些场景下可以避免竞争,提高并发性能,并减少对显式的依赖。但是,RCU 适用于特定的场景和数据访问模式,需要开发者根据具体情况进行合理使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

享乐主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值