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

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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)

以上是关于linux内核源码分析之内存屏障和RCU机制的主要内容,如果未能解决你的问题,请参考以下文章

Linux 内核 内存管理优化内存屏障 ③ ( 编译器屏障 | 禁止 / 开启内核抢占 与 方法保护临界区 | preempt_disable 禁止内核抢占源码 | 开启内核抢占源码 )

Linux 内核 内存管理优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )

Linux 内核 内存管理优化内存屏障 ① ( barrier 优化屏障 | 编译器优化 | CPU 执行优化 | 优化屏障源码 barrier 宏 )

linux内核源码分析之RCU

Linux 内核 内存管理优化内存屏障 ④ ( 处理器内存屏障 | 八种处理器内存屏障 | 通用内存屏障 | 写内存屏障 | 读内存屏障 | 数据依赖屏障 | 强制性内存屏障 |SMP内存屏障 )

Linux 内核 内存管理优化内存屏障 ④ ( 处理器内存屏障 | 八种处理器内存屏障 | 通用内存屏障 | 写内存屏障 | 读内存屏障 | 数据依赖屏障 | 强制性内存屏障 |SMP内存屏障 )