> 文档中心 > linux内核源码分析之内存屏障和RCU机制

linux内核源码分析之内存屏障和RCU机制

目录

一、优化内存屏障

二、RCU(Read-Copy-Update)


一、优化内存屏障

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)

书本网