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变体。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Li65LqG57u05oqk5LiW55WM5ZKM5bmzXw==,size_7,color_FFFFFF,t_70,g_se,x_16

 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();
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux 内核RCU(Read-Copy Update)机制在内存管理方面有一些特殊的考虑和实现。下面是关于 Linux 内核 RCU 内存管理的详细介绍: 1. RCU 内存回收:RCU 机制允许在没有锁的情况下进行并发读取,这意味着在进行内存回收时需要特殊处理。内核使用了一种称为“延迟回收”的技术来处理内存回收。当某个对象不再被引用时,RCU 并不立即释放它,而是延迟一段时间,以确保没有正在执行的读取操作仍然可以访问该对象。只有在延迟期间所有的读取操作都完成后,该对象才会被安全地释放。 2. RCU 保护数据结构:在使用 RCU 时,需要特殊的数据结构来管理共享数据。常见的结构包括 rcu_head、struct rcu_head、rcu_node 等。这些结构用于追踪需要进行延迟回收的对象,并在适当的时机释放它们。 3. RCU内存屏障:为了确保并发读取的正确性,RCU 引入了内存屏障(memory barrier)来保证读取操作的顺序。在读取共享数据之前和之后,需要使用适当的内存屏障指令来确保读取操作的正确顺序。 4. RCU 的内存分配:在使用 RCU 时,需要特殊的内存分配函数来分配 RCU 保护数据结构。Linux 内核提供了一些 RCU 特定的内存分配函数,例如 rcu_alloc() 和 rcu_free(),这些函数确保分配和释放的对象能够正确地与 RCU 机制配合使用。 5. RCU 的内存同步:在使用 RCU 时,需要进行适当的内存同步操作来确保数据的一致性。常见的同步操作包括 synchronize_rcu() 和 call_rcu()。synchronize_rcu() 用于等待所有当前正在执行的 RCU 读取操作完成,而 call_rcu() 用于注册一个回调函数,在所有当前正在执行的读取操作完成后执行。 总体而言,Linux 内核RCU 机制在内存管理方面提供了一种高效且无锁的并发读取解决方案。通过合理使用延迟回收、特殊数据结构、内存屏障和内存分配函数,以及适当进行内存同步操作,RCU 确保了并发读取的正确性和性能。这些特性使得 RCU 成为内核中重要的并发编程机制之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了维护世界和平_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值