《Android深度探索 卷1 HAL与驱动开发》笔记之Linux驱动程序中的并发操作(5)——读-复制-更新(RCU)机制原理

RCU的原理

RCU(Read-Copy-Update,读-复制-更新)机制可以看做是读写自旋锁的扩展。在rwlock机制中读自旋锁和写自旋锁时互斥的,但是在RCU机制中读和写操作是可以并发执行的。

在访问共享数据的时候,需要先获取锁,才能访问共享数据。这种原理很简单,其根本思想就是在访问共享资源时,需要先访问一个全局的变量,也就是锁,然后通过全局变量的状态来控制任务线程或进程对共享数据的访问。但是,这种思想是需要CPU进行支持的,CPU需要配合实现这个全局变量的读写操作。

由此又造成了如下两个问题的产生:

  • 效率问题

    锁机制的实现需要对内存进行原子化的访问,这种访问操作会破坏CPU的流水线式的操作,降低流水线效率,这是影响性能的一个关键因素。另外,在采用读写锁机制的情况下,写自旋锁是具有排他性的锁,无法实现写自旋锁和读自旋锁的并发操作,在某些应用中会降低系统性能。
  • 扩展性问题

**当系统中的CPU数量增多的时候,采用锁机制实现数据的同步访问会导致效率变低,并且随着CPU的数量越来越多,效率会越来越低,由此可见锁机制的扩展性是比较差的。

为了解决上述的两个问题,Linux内核引入了RCU机制。该机制在多CPU的平台上,特别是读多写烧的应用中特别适用。

RCU机制中的读操作

RCU机制中的读操作,可以直接对共享数据进行访问。因为RCU的读操作上下文是不可抢占的,所以读共享数据时,可以采用read_rcu_lock() 函数,这个函数的功能就是停止抢占。

RCU机制中的写操作

RCU机制中的写操作 ,在对共享数据进行写操作之前,需要对要修改的旧数据进行备份(copy),然后修改备份数据。修改完成后,再将修改后的备份数据更新真正要修改 的旧数据。更新旧数据会采用到 rcu_assign_point 宏。要注意的是,使用新数据更新旧数据的过程并不是用新数据直接覆盖旧数据,而是改变原来指向旧数据的指针,将指针指向新的数据。在修改完数据指针后,后台用于回收数据资源的线程机会利用想系统注册的回收函数来回收旧数据。

RCU机制的优越性

采用数据备份的方法可以解决读写共享数据之间发生的并发问题,但是不能解决对共享数据多次写操作之间的并发问题。也就是说,使用RCU机制同时只能有一个线程对共享数据进行写操作。这一点也可以看做是对读写自旋锁(rwlock)的升级版。因为RCU可以读写同时进行,而rwlock机制的读写是不能同时进行的

RCU机制中的垃圾回收线程

在RCU机制中存在一个垃圾回收的daemon(后台线程),当对共享数据进行写操作是,可以采用这个daemon实现对旧数据的回收工作。旧数据被回收的时间点就是在进行写操作前的所有读操作全部退出。由此可见,进行写操作修改了备份数据之后需要睡眠等待,直到要替换的旧数据没有被进行任何的读操作才能更新旧数据。如果有任何任务线程或进程的读操作没有退出(正在读取共享数据)时就会被抢占或者睡眠,这就很有可能会导致系统出现死锁的情况

RCU机制的实现流程

使用RCU机制可以并发的读取共享数据,而对共享数据进行写操作的时候需要进行如下4步:

  • 第1步:备份旧数据(产生资源副本);
  • 第2步:通过写操作修改备份的旧数据;
  • 第3步:等待条件被满足(旧数据没有被任何线程或进程使用),更新旧数据;
  • 第4步:通过daemon回收旧数据(指针由旧数据指向被修改的备份旧数据)。

RCU机制读写共享数据的原理

RCU机制的总结

RCU可以看做是读写自旋锁(rwlock)的高性能版本,比起读写自旋锁,RCU的优点是既允许多个对共享数据进行的读操作同时执行,又允许多个对共享数据进行读操作和写操作的线程同时执行。但是,RCU并不能替代读写自旋锁,因为如果写操作比较多的情况下,对读操作的性能提高是不能弥补写操作导致的性能消耗的。因为使用RCU时,多个写操作之间的同步开销会很大,它需要延迟数据结构的释放,赋值被修改的数据结构,它也必须使用某种锁机制来同步并发其他对共享数据进行修改的写操作。

发布了23 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/karaskass/article/details/102460598
今日推荐