linux内核源码分析之内存屏障和RCU机制
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux内核源码分析之内存屏障和RCU机制相关的知识,希望对你有一定的参考价值。
目录
一、优化内存屏障
1,编译器优化:为提高系统性能,编译器在不影响逻辑的情况下回调整指令的执行顺序
2,CPU优化:为提高流水线性能,CPU的乱序可能会让后面的寄存器冲突的指令由于前面完成
3,内存屏障:内存屏障是一种保证内存以正确的顺序访问
- 编译器编译代码可能会重排汇编指令,有时候优化结果不符合软件开发者意图
- 新式处理器采用超标量系统结构和乱序执行技术,能够在一个周期内并行执行多条指令顺序取指令,乱序执行,顺序提交执行结果
- 多处理器系统当中,硬件工程师使用存储缓冲区,缓存一致性实现高校性能,引入处理器之间的内存访问乱序问题。
解决方法
1)GCC编译器定义的宏
#define barrier() __asm__ __volatile__("": : :"memory")
#define preempt_enable() \\
do \\
barrier(); \\
if (unlikely(preempt_count_dec_and_test())) \\
__preempt_schedule(); \\
while (0)
2)处理器内存屏障
处理器内存屏障解决CPU之间的内存访问乱序问题 和 处理器访问外设的乱序问题
内存屏障类型 | 强制性内存屏障 | SMP内存屏障 |
通用内存屏障 | mb() | smp_mb() |
写内存屏障 | wmb() | smp_wmb() |
读内存屏障 | rmb() | smp_rmb() |
数据依赖屏障 | read_barrier_depends() | smp_barrier_depends() |
除了数据依赖屏障之外,所有处理器内存屏障隐含编译器优化屏障
二、RCU(Read-Copy-Update)"读-拷贝-更新"重要同步机制。
Linux内核已有原子操作,读写信号量等锁机制,为啥还有RCU呢?
1、RCU机制
RCU记录所有指向共享数据的指针的使用者,当要修改共享数据时,首先创建一个副本,在副本中修改。所有读访问线程都离开读临界区后,指针指向新的修改后副本的指针,并且删除旧数据。
使用场景:
- RCU经常用于读者性能要求比较高的场景,对写者的性能没有要求
- RCU只能保护动态分配的数据结构
- 必须是通过指针访问此数据结构
- 受RCU保护的临界区内不能sleep
缺点:
- 写者同步开销大,写者之间需要互斥操作,应用比较复杂
2、链表操作
RCU能保护的不仅是一般的指针。linux内核提供标准函数,使得通过RCU机制保护双链表,这是RCU机制在linux内核内部最重要的应用。只有在遍历、修改、删除链表元素是,必须调用标准函数的RCU变体。
rcu链表实现
//add a new entry to rcu-protected list
//插入
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
__list_add_rcu(new, head, head->next);
static inline void __list_add_rcu(struct list_head *new,
struct list_head *prev, struct list_head *next)
if (!__list_add_valid(new, prev, next))
return;
new->next = next;
new->prev = prev;
rcu_assign_pointer(list_next_rcu(prev), new);
next->prev = new;
#define rcu_assign_pointer(p, v) \\
do \\
uintptr_t _r_a_p__v = (uintptr_t)(v); \\
rcu_check_sparse(p, __rcu); \\
\\
if (__builtin_constant_p(v) && (_r_a_p__v) == (uintptr_t)NULL) \\
WRITE_ONCE((p), (typeof(p))(_r_a_p__v)); \\
else \\
smp_store_release(&p, RCU_INITIALIZER((typeof(p))_r_a_p__v)); \\
while (0)
#define smp_store_release(p, v) \\
do \\
compiletime_assert_atomic_type(*p); \\
barrier(); \\
WRITE_ONCE(*p, v); \\
while (0)
3,访问列表实例:
static void __maybe_unused update_runtime_enabled(struct rq *rq)
struct task_group *tg;
lockdep_assert_held(&rq->lock);
rcu_read_lock();
list_for_each_entry_rcu(tg, &task_groups, list)
struct cfs_bandwidth *cfs_b = &tg->cfs_bandwidth;
struct cfs_rq *cfs_rq = tg->cfs_rq[cpu_of(rq)];
raw_spin_lock(&cfs_b->lock);
cfs_rq->runtime_enabled = cfs_b->quota != RUNTIME_INF;
raw_spin_unlock(&cfs_b->lock);
rcu_read_unlock();
(内核免费课程链接:https://ke.qq.com/course/4032547?flowToken=1042391)
以上是关于linux内核源码分析之内存屏障和RCU机制的主要内容,如果未能解决你的问题,请参考以下文章
Linux 内核 内存管理优化内存屏障 ③ ( 编译器屏障 | 禁止 / 开启内核抢占 与 方法保护临界区 | preempt_disable 禁止内核抢占源码 | 开启内核抢占源码 )
Linux 内核 内存管理优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )
Linux 内核 内存管理优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )
Linux 内核 内存管理优化内存屏障 ④ ( 处理器内存屏障 | 八种处理器内存屏障 | 通用内存屏障 | 写内存屏障 | 读内存屏障 | 数据依赖屏障 | 强制性内存屏障 |SMP内存屏障 )
Linux 内核 内存管理优化内存屏障 ④ ( 处理器内存屏障 | 八种处理器内存屏障 | 通用内存屏障 | 写内存屏障 | 读内存屏障 | 数据依赖屏障 | 强制性内存屏障 |SMP内存屏障 )