RCU机制

RCU英文全称为Read-Copy-Update,顾名思义就是 “读 - 拷贝-更新”,是内核中重要的同步机制。

RCU原理

RCU记录所有指向共享数据的指针的使用者,当要修改该共享数据时,首先创建一个副本,在副本中修改。所有读访问线程都离开读临界区之后 ,指针指向新的修改后副本的指针,并且删除旧数据。

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

内存条和bpu使用率有关系吗(一文解决RCU机制及内存优化屏障)(1)

内存条和bpu使用率有关系吗(一文解决RCU机制及内存优化屏障)(2)

Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈-学习视频教程-腾讯课堂

链表操作

RCU能保护的不仅仅是一般的指针。内核也提供标准函数,使得能通过RCU机制保护 双链表,这是RCU机制在内核内部最重要的应用。

//添加新元素到链表头部 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_tail_rcu(struct list_head *new, struct list_head *head) { __list_add_rcu(new, head->prev, head); } //从链表删除元素entry static inline void list_del_rcu(struct list_head *entry) { __list_del_entry(entry); entry->prev = LIST_POISON2; } //链表元素替换 static inline void list_replace_rcu(struct list_head *old, struct list_head *new) { new->next = old->next; new->prev = old->prev; rcu_assign_pointer(list_next_rcu(new->prev), new); new->next->prev = new; old->prev = LIST_POISON2; }

对于writer,RCU操作包括:

  1. rcu_assign_pointer 该函数被writer用来进行removal操作,在writer完成新版数据分配和更新之后,调用这个函数可以记RCU protected pointer指向RCU protected data。
  2. synchronize_rcy: writer端操作可以是同步的,也就是说,完成更新操作之后,可以调用这个函数等到所有旧版本数据上的reader线程离开临界区,一旦从函数返回,说明就得共享数据没有任何引用,直接进行reclaimation的操作。
  3. call_rcu:writer无法阻塞,可以调用call_rcu接口函数,该函数是注册callback直接返回,在适当实际会调用callback函数,完成reclaimation。

内存条和bpu使用率有关系吗(一文解决RCU机制及内存优化屏障)(3)

  1. removal:write分配一个new version共享数据进行数据更新,更新完毕后将pointer指向新版本的数据,通过这样的操作,原来reader0 reader1对共享数据的引用被删除。reclamation:共享数据不能有两个版本,一定要在适当的而实际回收旧版本数据。
RCU层次架构

RCU根据CPU数量的大小按照树形结构来自成其层次结构,称为RCU Hierarchy。

#ifdef CONFIG_RCU_FANOUT //每个层数最多支持的叶子数量 #define RCU_FANOUT CONFIG_RCU_FANOUT #else /* #ifdef CONFIG_RCU_FANOUT */ # ifdef CONFIG_64BIT # define RCU_FANOUT 64 # else # define RCU_FANOUT 32 # endif #endif /* #else #ifdef CONFIG_RCU_FANOUT */ #ifdef CONFIG_RCU_FANOUT_LEAF //一个子叶子的CPU数量 #define RCU_FANOUT_LEAF CONFIG_RCU_FANOUT_LEAF #else /* #ifdef CONFIG_RCU_FANOUT_LEAF */ # ifdef CONFIG_64BIT # define RCU_FANOUT_LEAF 64 # else # define RCU_FANOUT_LEAF 32 # endif #endif /* #else #ifdef CONFIG_RCU_FANOUT_LEAF */

【32核处理器的CPU层次结构】两个层次,level0 包含两个struct rcu_node, 每个struct rcu_node管理16个struct rcu_data数据结构,分别表示16个CPU独立的struct rcu_data数据结构;在level1层级,有一个struct rcu_node节点管理level0层级的两个rcu_node节点,level1层级中的rcu_node节点称为根节点,level0层级的连个rcu_node节点是叶子节点。

内存条和bpu使用率有关系吗(一文解决RCU机制及内存优化屏障)(4)

优化屏障
  1. 编译器优化:提高系统性能,编译器在不影响逻辑的情况下会调整指令的顺序。
  2. CPU执行优化:提高流水线性能,CPU的乱序执行可能会让后面的没有寄存器冲突和汇编指令优于前面的指令完成。
  3. 优化屏障避免编译的重新排序优化操作,保证编译程序时在优化屏障之前的指令不会 在优化屏障之后执行。

/* Optimization barrier */ /* The "volatile" is due to gcc bugs */ #define barrier() __asm__ __volatile__("": : :"memory") //__asm__表示插入汇编语言程序;__volatile__表示组织编译对该值进行优化,确保变量使用了用户定义的精确地址,而不是装有同一信息的一些别名。memory表示修改了内存单元。

内存屏障

内存屏障,也称内存栅障或屏障指令等,是一类同步屏障指令,是编译器或CPU对内存访问操作的时候,严格按照一定顺序来执行,也就是memory barrier之前的指令和memory barrier之后的指令不会由于系统优化等原因而导致乱序。memory barrier包括两类:编译器屏障(complier barrier)和CPU屏障(cpu barrier)

The Linux kernel has eight basic CPU memory barriers: TYPE MANDATORY SMP CONDITIONAL =============== ======================= =========================== GENERAL(保证读写操作有序) mb() smp_mb() WRITE(仅保证写操作有序) wmb() smp_wmb() READ(仅保证读操作有序) rmb() smp_rmb()

mb()/rmab()/wmb()将硬件内存屏障插入到代码流程中,barrier插入一个优化屏障,该指令告知编译器,保存在CPU寄存器中、在屏障之前有效的所有内存地址,在屏障之后全部消失。本质,编译器在屏障之前发出的读写请求完成之前,会处理屏障之后的任何读写请求。smp_mp()/smp()_rmb()/smp_wmb()相当于硬件内存屏障,只适用于SMP系统。在单处理器系统上产生的是软件屏障。内存屏障作用:解决CPU高速缓存存在的问题,无锁数据结构,内存屏障很有用处。

总结

本文主要介绍了RCU机制,包括原理、链表操作、层次架构、32核处理器的CPU层次结构,及内存屏障,包括编译器屏障、CPU屏障等。

- - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛

转载地址:一文解决RCU机制及内存优化屏障 - 圈点 - 内核技术中文网 - 构建全国最权威的内核技术交流分享论坛

内存条和bpu使用率有关系吗(一文解决RCU机制及内存优化屏障)(5)

,