linux内核源码分析之RCU
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之RCU相关的知识,希望对你有一定的参考价值。
目录
RCU概念
RCU(Read Copy-Update),读-复制更新,是一种同步机制。它是在2.5开发过程中添加到Linux内核的一种同步机制,该机制针对以读为主的情况进行了优化。
读临界区:每个临界区开始与rcu_read_lock(),结束于rcu_read_unlock()。这些指针函数实现了依赖顺序加载的概念。
写临界区:推迟销毁并维护多个版本的数据结构,有同步开销。
RCU核心思想:
1)复制后更新;2)延迟回收内存
- 删除指向数据结构的指针,以便后续读取器无法获得对它的引用。
- 等待所有之前的读者完成他们的RCU阅读端关键部分。
- 此时,不可能有任何读者持有对数据结构的引用,因此现在可以安全地回收它 例如,kfree()d)。
使用场景
- 多读少写
- RCU保护的是指针,因为指针赋值是一条单指令
- 对数据没有强一致性要求
- 不能发生进程上下文切换,可能阻塞
核心函数
//读锁 preempt_disable
rcu_read_lock()
//读解锁 preempt_enable
rcu_read_unlock()
//注册一个函数和自变量,这些函数和自变量在所有正在进行的RCU读取侧关键部分均已完成之后被调用
synchronize_rcu()/call_rcu()
//将新指针赋给RCU结构体,赋值前的读者看到的还是旧的指针
rcu_assign_pointer()
//获取受保护的RCU指针
rcu_dereference()
本节展示了如何简单使用核心RCU API来保护指向动态分配结构的全局指针
实例一
struct foo
int a;
char b;
long c;
;
DEFINE_SPINLOCK(foo_mutex);
struct foo __rcu *gbl_foo;
void foo_update_a(int new_a)
struct foo *new_fp;
struct foo *old_fp;
new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);
spin_lock(&foo_mutex);
old_fp = rcu_dereference_protected(gbl_foo, lockdep_is_held(&foo_mutex));
*new_fp = *old_fp;
new_fp->a = new_a;
rcu_assign_pointer(gbl_foo, new_fp);
spin_unlock(&foo_mutex);
synchronize_rcu();
kfree(old_fp);
int foo_get_a(void)
int retval;
rcu_read_lock();
retval = rcu_dereference(gbl_foo)->a;
rcu_read_unlock();
return retval;
- 使用rcu_read_lock()和rcu_read_unlock()来保护RCU,读侧关键部分
- 在RCU读取端临界区内,使用RCU_dereference()来解除对受RCU保护的指针的引用。
- 使用一些可靠的方案(例如锁或信号量)来防止并发更新相互干扰
- 使用rcu_assign_pointer()更新受rcu保护的指针;
- 使用synchronize_rcu()从受rcu保护的数据结构中删除数据元素
实例二
在上面的例子中,foo_update_a()会一直阻塞,直到宽限期结束。这很简单,但在某些情况下,人们不能等待太久,可能还有其他高优先级的工作要做。
void call_rcu(struct rcu_head * head,
void (*func)(struct rcu_head *head));
此函数在宽限期过后调用func,此调用可能发生在softirq或进程上下文中,因此不允许函数阻塞
struct foo
int a;
char b;
long c;
struct rcu_head rcu;
;
void foo_update_a(int new_a)
struct foo *new_fp;
struct foo *old_fp;
new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);
spin_lock(&foo_mutex);
old_fp = rcu_dereference_protected(gbl_foo, lockdep_is_held(&foo_mutex));
*new_fp = *old_fp;
new_fp->a = new_a;
rcu_assign_pointer(gbl_foo, new_fp);
spin_unlock(&foo_mutex);
call_rcu(&old_fp->rcu, foo_reclaim);
void foo_reclaim(struct rcu_head *rp)
struct foo *fp = container_of(rp, struct foo, rcu);
foo_cleanup(fp->a);
kfree(fp);
使用call_rcu()确保在释放旧结构之前,所有可能引用旧结构的读都已完成。
rcu_read_lock函数的实现是 preempt_disable
static inline void __rcu_read_lock(void)
preempt_disable();
实例三
模块创建三个内核线程,两个读线程,一个写线程
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/kthread.h>
#include <linux/delay.h>
struct ftestrcu
int a;
struct rcu_head rcu;
;
static struct ftestrcu *g_ptr;
static int myrcu_reader_thread1(void *data)
struct ftestrcu *p1 = NULL;
while (1)
if (kthread_should_stop())
break;
msleep(10);
rcu_read_lock();
mdelay(100);
p1 = rcu_dereference(g_ptr);
if (p1)
printk("%s: Read1 a1=%d\\n", __func__, p1->a);
rcu_read_unlock();
return 0;
static int myrcu_reader_thread2(void *data)
struct ftestrcu *p2 = NULL;
while (1)
if (kthread_should_stop())
break;
msleep(20);
rcu_read_lock();
mdelay(200);
p2 = rcu_dereference(g_ptr);
if (p2)
printk("%s:Read2 a2=%d\\n", __func__, p2->a);
rcu_read_unlock();
return 0;
static void myrcu_del(struct rcu_head *rh)
struct ftestrcu *p = container_of(rh, struct ftestrcu, rcu);
printk("%s: a=%d\\n", __func__, p->a);
kfree(p);
static int myrcu_writer_thread(void *p)
struct ftestrcu *old;
struct ftestrcu *new_ptr;
int value = (unsigned long)p;
while (1)
if (kthread_should_stop())
break;
msleep(300);
new_ptr = kmalloc(sizeof(struct ftestrcu), GFP_KERNEL);
old = g_ptr;
*new_ptr = *old;
new_ptr->a = value;
rcu_assign_pointer(g_ptr, new_ptr);
call_rcu(&old->rcu, myrcu_del);
printk("%s: Write to new %d\\n", __func__, value);
value++;
return 0;
static struct task_struct *reader_thread1;
static struct task_struct *reader_thread2;
static struct task_struct *writer_thread;
static int __init myrcu_init(void)
int value = 5;
printk("\\n\\nRCU Module Init OK.\\n");
g_ptr = kzalloc(sizeof(struct ftestrcu), GFP_KERNEL);
reader_thread1 = kthread_run(myrcu_reader_thread1, NULL, "Rcu_reader1");
reader_thread2 = kthread_run(myrcu_reader_thread2, NULL, "Rcu_reader2");
writer_thread = kthread_run(myrcu_writer_thread, (void *)(unsigned long)value, "Rcu_Writer");
return 0;
static void __exit myrcu_exit(void)
printk("\\nRCU Unloading Module Exit.\\n\\n");
kthread_stop(reader_thread1);
kthread_stop(reader_thread2);
kthread_stop(writer_thread);
if (g_ptr)
kfree(g_ptr);
module_init(myrcu_init);
module_exit(myrcu_exit);
MODULE_AUTHOR("Wy");
MODULE_LICENSE("GPL");
dmesg 查看打印输出
[130138.082438] myrcu_reader_thread1: Read1 a1=0
[130138.189646] myrcu_reader_thread2:Read2 a2=0
[130138.198330] myrcu_reader_thread1: Read1 a1=0
[130138.270907] myrcu_writer_thread: Write to new 5
[130138.314884] myrcu_reader_thread1: Read1 a1=5
[130138.414074] myrcu_reader_thread2:Read2 a2=5
[130138.419551] myrcu_del: a=0
[130138.433998] myrcu_reader_thread1: Read1 a1=5
[130138.550795] myrcu_reader_thread1: Read1 a1=5
[130138.591066] myrcu_writer_thread: Write to new 6
[130138.638104] myrcu_reader_thread2:Read2 a2=6
[130138.670482] myrcu_reader_thread1: Read1 a1=6
[130138.786063] myrcu_reader_thread1: Read1 a1=6
[130138.861480] myrcu_reader_thread2:Read2 a2=6
[130138.867331] myrcu_del: a=5
[130138.901986] myrcu_reader_thread1: Read1 a1=6
[130138.915200] myrcu_writer_thread: Write to new 7
[130139.018513] myrcu_reader_thread1: Read1 a1=7
[130139.085237] myrcu_reader_thread2:Read2 a2=7
[130139.133893] myrcu_reader_thread1: Read1 a1=7
[130139.235345] myrcu_writer_thread: Write to new 8
[130139.250811] myrcu_reader_thread1: Read1 a1=8
[130139.309200] myrcu_reader_thread2:Read2 a2=8
参考
内核源码:Documentation/RCU/whatisRCU.rst
Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂 (qq.com)
以上是关于linux内核源码分析之RCU的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理RCU 机制 ⑤ ( RCU 层次架构概念 | RCU 层次架构源码解析 | RCU 层次架构每层最多叶子数 | RCU 层次架构每个叶子 CPU 数量 )