RCU

RCU(Read-Copy-Update)是一种在Linux内核中广泛使用的同步机制,尤其适用于读远多于写的场景。其工作原理是通过复制要修改的共享结构体并等待所有读操作完成后,再更新指针,避免读写冲突。宽限期是RCU处理读写冲突的关键,确保所有在修改前开始的读操作完成后,才进行实际更新。在内核链表操作中,RCU也提供了如list_add_rcu等函数来保证安全性。
摘要由CSDN通过智能技术生成

1. 什么是RCU

RCU(Read-Copy-Update),是一种同步机制,它虽然对内存有一定的开销,但是它的性能非常好。在Linux内核中随处可见它的身影。

2. RCU的工作机制

RCU下,会记录指向共享结构体指针的所用使用者。这个结构体将要被修改时,首相将会创建一个副本,然后把改动写入副本当中。当所有的读操作使用者访问结束之后,指针指向新的修改后副本的指针,副本就这么上位了,修改也就是最新的了。基本原理是很简单的,一个备胎而已,空闲时上位而已。要是支持多任务读写并发,就复杂些了。

3. 适用场景

RCU不是万能的,它也只是某些特定场景。如果同时的写入者有多个,那么RCU是不是需要多个副本?显然这种情况它的效率是很低的。RCU是有自己的约束的,大致有以下三条

  • 受保护的资源必须通过指针访问
  • RCU保护范围内的,内核不能进入休眠状态
  • 对共享区的访问,应该是读远远大于写,或者说写很少的场景

4. 就这么简单吗?核心问题,怎么解决读写冲突的?

如下图,当“修改"线程进行操作时,读线程1和读线程2还未结束。这时候"删除"线程会进入宽限期,这是非常重要的一个概念。宽限期截止到最后一个读线程结束,之后"修改"线程完成事实更新。宽限期内开始的线程3,4,6 也不会读到修改后的数据,它读的是副本的数据。

 

5. RCU的核心API

5.1 读操作相关

首先ptr指向被共享区,p必须通过rcu_dereference才能反引用返回的结果,并且必须在rcu_read_lock和rcu_read_unlock之间做非写访问,否则不被保护。

rcu_read_lock();
p = rcu_dereference(ptr);
if(p){
    awesome_function(p);
}
rcu_read_unlock();

5.2 修改相关操作

rcu_assign_pointer 公布这个指针之后,后续的操作(比如线程三四六)将看到新的结构,而不是原来的。在所有读访问结束之后,内核释放旧实例内存。如果想在所有读结束之后,访问共享资源,可以调用synchronize_rcu,它将阻塞直到所有读完成。

struct super_duper *new_ptr = kmalloc(...)
new_ptr->data = 0xA5A5;
rcu_assign_pointer(ptr, new_ptr);

 

 

6. 在内核链表当中的应用

 

RCU保护的链表,只有在遍历、修改删除元素时,调用RCU的变体,如上图也就是标准函数后加_rcu后缀。

static inline void list_add_rcu(struct list_head *new,struct list_head *head);
static inline void list_add_tail_rcu(struct list_head *new,struct list_head *head);
static inline void list_del_rcu(struct list_head *entry);
static inline void list_replace_rcu(struct list_head *old,struct list_head *new);

增加元素

#define list_next_rcu(list)     (*((struct list_head __rcu **)(&(list)->next)))

static inline void __list_add_rcu(struct list_head *new,
                struct list_head *prev, struct list_head *next)
{
        new->next = next;
        new->prev = prev;
        rcu_assign_pointer(list_next_rcu(prev), new);
        next->prev = new;
}

访问链表要用以下方式

rcu_read_lock();
list_for_each_entry_rcu(pos, head, member) {
    // do something with `pos`
}
rcu_read_unlock();

 

参考:

[0] 深入Linux内核架构

[1] https://blog.csdn.net/junguo/article/details/8244530

 

在 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 适用于特定的场景和数据访问模式,需要开发者根据具体情况进行合理使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值