linux代码之RCU

/*
 * 2020/12/4    20:25    qing
 */

/*
 * RCU
 */

读(Read):读者不需要获得任何锁就可访问RCU保护的临界区;

拷贝(Copy):写者在访问临界区时,写者“自己”将先拷贝一个临界区副本,然后对副本进行修改;

更新(Update):RCU机制将在在适当时机使用一个回调函数把指向原来临界区的指针重新指向新的被修改的临界区,锁机制中的垃圾收集器负责回调函数的调用。

总结即是读-拷贝-更新。
    

信号量有一个很明显的缺点,没有区分临界区的读写属性,读写锁允许多个线程进程并发的访问临界区,但是写访问只限于一个线程,

在多处理器系统中允许多个读者访问共享资源,但是写者有排他性,读写锁的特性如下:允许多个读者同时访问临界区,但是同一时间不能进入;

同一时刻只允许一个写者进入临界区;读者和写者不能同时进入临界区。读写锁也有关闭中断和下半部的版本。

/*
 * code
 */
    kernel/rcu/*


/*
 * rcu相比读写锁,解决了什么问题
 */
    由于内核中spinlock mutex 等都使用了原子操作指令,即原子的访问内存,但是当多cpu 竞争访问临界区时会让cpu的cache命中率下降,性能下降。
    同时读写锁有个缺陷,读者和写者不能同时存在。

    rcu实现的目标就是要解决这个问题,为了使线程同步开销小。不需要原子操作以及内存屏障而访问数据,把同步的问题交给写者线程,
    写者线程等待所有的读者线程完成后才会吧旧数据销毁。当有多个写者线程存在时,需要额外的保护机制。

/*
 * RCU原理
 */
    简单理解为 记录了所有指向共享数据的指针使用者,当要修改共享数据时,先创建一个副本,在副本中修改。所有读者离开临界区后,
    指针指向新的修改副本后的地方,并且删除旧数据。

    目前在内核中链表使用RCU较多。

    RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用。RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,
    为了达到目的使用RCU机制读取数据的时候不对链表进行耗时的加锁操作。这样在同一时间可以有多个线程同时读取该链表,并且允许一个线程对链表进行修改
    (修改的时候,需要加锁)。"RCU适用于需要频繁的读取数据,而相应修改数据并不多的情景",例如在文件系统中,经常需要查找定位目录,
    而对目录的修改相对来说并不多,这就是RCU发挥作用的最佳场景。

    Linux内核源码当中,关于RCU的文档比较齐全,你可以在 /Documentation/RCU/ 目录下找到这些文件。Paul E. McKenney 是内核中RCU源码的主要实现者,
    他也写了很多RCU方面的文章。他把这些文章和一些关于RCU的论文的链接整理到了一起。
    
    http://www2.rdrop.com/users/paulmck/RCU/

        
    在经典RCU中,RCU读侧临界部分由rcu_read_lock() 和rcu_read_unlock()界定,它们可以嵌套。

    对应的同步更新原语为synchronize_rcu(),还有同义的synchronize_net(),等待当前正执行的RCU读侧闻临界部分运行完成。等待的时间称为“宽限期”。

    异步更新侧原语call_rcu()在宽限期之后触发指定的函数,如:call_rcu(p,f)调用触发回调函数f(p)。有些情况,如:当卸载使用call_rcu()的模块,
    必须等待所有RCU回调函数完成,原语rcu_barrier()起该作用。

    在“RCU BH”列中,rcu_read_lock_bh() 和rcu_read_unlock_bh()界定读侧临界部分,call_rcu_bh()在宽限期后触发指定的函数。
    注意:RCU BH没有同步接口synchronize_rcu_bh(),如果需要,用户很容易添加同步接口函数。

    直接操作指针的原语rcu_assign_pointer()和rcu_dereference()用于创建RCU保护的非链表数据结构,如:数组和树

/*
 * note
 */
    在RCU的实现过程中,我们主要解决以下问题:

        1,在读取过程中,另外一个线程删除了一个节点。删除线程可以把这个节点从链表中移除,但它不能直接销毁这个节点,必须等到所有的读取线程读取
           完成以后,才进行销毁操作。RCU中把这个过程称为宽限期(Grace period)。

        2,在读取过程中,另外一个线程插入了一个新节点,而读线程读到了这个节点,那么需要保证读到的这个节点是完整的。这里涉及到了发布-订阅
           机制(Publish-Subscribe Mechanism)。

        3,保证读取链表的完整性。新增或者删除一个节点,不至于导致遍历一个链表从中间断开。但是RCU并不保证一定能读到新增的节点或者不读到要
           被删除的节点。

猜你喜欢

转载自blog.csdn.net/xiaozhiwise/article/details/111215292
RCU