自旋锁 | 读写锁范式 悲观 | 顺序锁范式 乐观 | RCU 理智礼让沟通 j | |
---|---|---|---|---|
哲学 | 比较悲观,假设最坏情况, 认为读时有人写,读写冲突大 | 比较乐观,假设最好情况,认为读时没人写。 读写冲突小。 | 理智派。综合二者优缺点。认为读时,有人写,但是写比较理智礼让,会等待读完成。 | |
读写冲突的读写 | 有等待读写 | 有等待读写 | 无等待写,回退读 | 无等待读,有限等待写 |
读时机 | [now, ∞) | [now, ∞) after write | [now, ∞) after write and retry 可无限等待 等待读期间的写完成 and retry | now |
写时机 写忙等时间范围 | [now, ∞) | [now, ∞) | now | [now, grace period] 有限等待 等待写期间 [rcu_assign_pointer] grace preriod的读完成,无retry。 这里通过时序图来表达比较好。 |
读并发 | 无 | 有 | 有 | 有 |
读写优先级 | 读=写 | 读=写 | 写>读 | 读>写 |
副作用 | 无 | 无 | 读回退。死锁。有优先级,就有死锁可能(优先级反转,高优先级读写的却在 低优先级的进程里,通过禁止抢占解决),高优先(写)的必须要禁止抢占,果然write_seqcount_begin 有 preempt_disable,因需写写互斥,就进一步升级为 spin_lock() | 死锁,高优先级(读)必须要禁止抢占。果然 rcu_read_lock 里有 preempt_disable。禁止抢占,免得进程被调度走,解决优先级反转的问题。 |
优化场景 | 无 | 读并发 | 读并发,写>读 | 读并发,读>写 |
优点 | 相比自旋锁,读并发,写独占。 | 相比自旋锁,读并发,写独占。 无等待读写,但是读可能回退。 | ||
缺点 | 无法保证读优先。写饥饿。 | 频繁写,性能更差 | ||
读多写少,且要优先保证读性能! | 读多写少,且优先级 写>读。要优先保证写性能! | |||
场景举例 | ||||
并发读时的写性能 频繁读,写极少 | 写性能差。写要等待读完成。频繁读时,写很慢。 | 保证写性能,写能无等待完成。 | ||
并发读时的写性能 频繁写 最差情况 | 退化为自旋锁 | 造成频繁的读回退,性能比自旋锁还差。 | ||
read | write_lock … write_unlock | do { seqnum = read_seqcount_begin(&seqcount); … } while (read_seqcount_retry(&seqcount, seqnum)); | rcu_read_lock (=preempt_disable) old_ptr=rcu_dereference§ // 保存旧指针 进行读访问 rcu_read_unlock (=preempt_enable) | |
write | read_lock … read_unlock | write_seqcount_begin(&seqcount); … write_seqcount_end(&seqcount); | … Read Copy Update rcu_assign_pointer(ptr, new_ptr); synchronize_rcu() free(old_ptr) | |