RCU

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

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

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值