首页 > 其他分享 >技术分享 | RCU :内核小“马达”,让你的产品弯道超车

技术分享 | RCU :内核小“马达”,让你的产品弯道超车

时间:2023-08-29 17:02:15浏览次数:52  
标签:lock void rcu 内核 RCU 弯道 CPU 读端

在上一篇文章《编程界也内卷?浅析“斜杠青年”RCU 》中,鼎道智联带着大家一起认识了并行编程,了解了什么是 RCU ,相信大家已经对 RCU 的特点和如何实现 Reader 无锁有了一定的了解。

今天就带着大家继续从 RCU 的实现入手,一起看看在实际操作中,并行编程是如何实现的!

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_临界区

RCU 的实现原理可以概括为“读取-复制-更新”(Read-Copy-Update)。在 RCU 中,共享数据结构被复制多次,每个副本都有自己的读者,并且在写者更新数据结构之前,所有读者都只读取数据结构的一个副本。当写者更新数据结构时,它会创建一个新的副本,而不是直接更新原始数据结构。然后,写者会在新的副本上进行更新操作,并向所有正在使用旧副本的读者发出通知,让它们开始使用新的副本。由于所有的读者都是在旧副本上读取数据的,因此写者的更新不会影响正在读取旧副本的任何读者。一旦没有任何读者使用旧副本,它就可以被销毁,释放内存资源。

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_临界区_02

为了能让大家更好地理解 RCU 的实现原理,我们通过几种 RCU 的实现来说明这点。

首先,我们需要认识一些常用名词:

  • 静止状态:

Quiescent State,简写 qs ,在任意时刻,一个特定的 CPU 只要看起来处于阻塞状态、 IDLE 循环、或者离开内核的状态,我们就知道所有 RCU 读端临界区已经完成。这些状态被称为“静止状态”。

  • 宽限期:

grace period,简写 gp ,这是一个等待期,以确保所有与执行删除数据相关的 reader 访问完毕。

RCU 宽限期的开始和结束都取决于是否有读端在临界区内,因此,也可以将 RCU 的重要过程分为三个:

  • 读端进入临界区
  • 读端退出临界区
  • 等待宽限期结束(同步方式)

下面通过几个简单的例子来也重点看看上述三个方面是如何实现的:

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_数据结构_03

DEFINE_SPINLOCK(rcu_gp_lock);
static void rcu_read_lock(void)
{
spin_lock(&rcu_gp_lcok);
}
static void rcu_read_unlock(void)
{
spin_unlock(&rcu_gp_lock);
}
static void synchronize_rcu(void)
{
spin_lock(&rcu_gp_lock);
spin_unlock(&rcu_gp_lock);
}

上述的例子是通过 spinlock 的方式来实现:即 rcu_read_lock() 函数和 rcu_read_unlock() 函数通过对 spinlock 上锁和解锁,可以很好地判断出读端当前是否处于临界区内;在 synchronize_rcu() 函数中通过检查 spinlock 是否上锁来判断是否有读端在临界区内,即宽限期是否结束。

所以本例子很好地实现了 rcu_read_lock() 函数、rcu_read_unlock() 函数和 synchronize_rcu() 函数的语义。但是 spinlock 具有强烈的排他性,所以每次只能有一个读端进入临界区。

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_数据结构_04

为了更好地提高读端的并发行,我们首先想到了计数器,即通过计数器的值来判断是否所有读端都退出了临界区。

这时,我们采用了原子变量作为读端的计数器,那为什么使用原子变量作为计数器呢?

首先原子操作的性能开销要远远小于锁的性能开销,其次原子操作也只对计数器变量进行保护,所以在读端可以并发执行。

atomic_t rcu_refcnt;static void rcu_read_lock(void){
atomic_inc(&rcu_refcnt);smp_mb();
}
static void rcu_read_unlock(void){
smp_mb();atomic_dec(&rcu_refcnt);} static void synchronize_rcu(void){
smp_mb();
while(atomic_read(&rcu_refcnt) != 0) {
poll(NULL, 0, 10);
}
smp_mb();
}

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_临界区_05

从上述这个例子我们可以看到,使用原子变量作为计数器可以大幅提高读端的并发性能,但是该原子计数器仍然是一个全局变量,是被所有的读端共享的一个变量,因此,随着并发数量的增加,对原子计数器的竞争机率也会增加,这就导致了扩展性不足问题的出现。

但我们通过空间换性能的方式,将读端进入临界区的标记变成一个线程,这样读端之间就不存在共享进入临界区标记的情况,也提高了读端的并发性和扩展性。

static void rcu_read_lock(void)
{
spin_lock(&__get_thread_var(rcu_gp_lock));
}
static void rcu_read_unlock(void)
{
spin_unlock(&__get_thread_var(rcu_gp_lock));
}
static void synchronize_rcu(void)
{
for_each_running_thread(t){
spin_lock(&per_thread(rcu_gp_lock, t));
spin_unlock(&per_thread(ruc_gp_lock, t));
}
}

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_原子变量_06

在 Linux 内核中 RCU 有多种实现方式,根据跟踪宽限期的方式不同可以简单分为:基于单核的 tiny 实现、基于静止状态的实现、基于任务的实现、和可睡眠的 SRCU 等。我们简单以基于静止状态的 RCU 的宽限期跟踪实现为例子,为大家介绍。

经典 RCU 读端临界区限制其中的内核代码,不允许其阻塞。这意味着在任意时刻,一个特定的 CPU 只要看起来处于阻塞、 IDLE 循环、或离开内核的状态,就可以确定所有 RCU 读端临界区都已完成——我们将这些状态称为“静止状态”;当每一个 CPU 都经历过至少一次静止状态时,则代表 RCU 宽限期结束。

在 Linux 中经典的 RCU 实现就巧妙地利用了上述的原则:通过对静止状态的检测来减少读端的竞争。为了减少 CPU 上报时对数据锁的竞争, RCU 采用了分级控制,如下图:

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_原子变量_07

在一个 rcu_node 中,第一个上报静止状态的 CPU 会将数据保存在该层的节点中;只有另外一个 CPU 也上报了静止状态的时候,才会将该节点的两个 CPU 的静止状态同时上报到上一层的 rcu_node 中。这样做的好处是:在底层同一时间至多有两个 CPU 会竞争锁,这也减少了顶层锁的竞争,从而大大减少 CPU 相互竞争的机会。

技术分享 | RCU :内核小“马达”,让你的产品弯道超车_数据结构_08

其实,除了静止状态下的非阻塞算法之外, RCU 还有多种实现来减少 CPU 之间的相互竞争:

  • RCU 采用延迟回收技术:

RCU 采用延迟回收技术来销毁旧的副本,这种技术可以将内存释放延迟到一定时机,从而减少了 CPU 之间的相互竞争。

  • RCU 允许并发读取:

RCU 允许多个读者同时访问共享数据结构的一个副本,从而减少了 CPU 之间的相互竞争。尤其在多核 CPU 上,这可以提高程序的并发性能。

我们可以看到无论哪种方式, RCU 都在努力减少 CPU 之间的相互竞争,通过这种方式不仅可以提升程序的并发性能,还可以提高响应速度、程序的可扩展性;除此之外, RCU 还可以减少锁的使用,一部分降低了锁竞争的开销,另一部分还可以降低程序的复杂度。由于 RCU 不需要使用锁,因此程序的代码会更加简单,也减少了锁的处理逻辑和异常情况的处理。这有助于提高代码的可维护性和可读性,从而减少出错的可能性。

在互联网行业越来越卷的今天,更快的响应速度、更低的代码成本就代表着有越多的可能性,来开发出专属于自己产品的壁垒优势。

对于鼎道智联来说,开发一个全新的、以人为本的、为用户提供更便捷、智能、安全的操作系统,就代表着我们需要走更多创新的路,需要花更多时间去钻研市场动态、新兴技术,鼎道智联一直保持着对行业的探索和对自己生态的打造,如果你也认可我们的想法,有相同的目标,欢迎加入或关注鼎道生态。


标签:lock,void,rcu,内核,RCU,弯道,CPU,读端
From: https://blog.51cto.com/u_15368321/7278116

相关文章

  • ch2_ab1 Linux内核模块
    准备事项:虚拟机系统文件传输工具VMwareCentOSWinscpVitualBoxUbuntuXftp有222种选择,我是Virtualbox+Ubuntu+Winscp下载osc前言中的Linux系统osc10e.ova安装VirtualBox导入osc10e.ovaSilberschatz,Galvin,Gagne:OperatingSystemC......
  • 我的Linux网络之行2-初探内核网络
    前言在上一文中,初步了解了一些协议与相关的一些知识,至少已经有了一些概念,OK,那么下面就是我们对于开发的深入了。Linux以太网驱动架构OSI模型中将网络划分为七层,从下到上依次为:物理层(Physical)、数据链路层(DateLink)、网络层(Network)、传输层(Transport)、会话层(Session)、表示层(Pre......
  • 16 Linux 内核定时器实验
    一、Linux时间管理和内核定时器简介1.内核时间管理简介  Linux内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、定时器等。  硬件定时器提供时钟源,时钟源的频率可以设置,设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率......
  • 8步轻松晋级AMD MPSoC Linux内核调试专家
    8步轻松晋级AMDMPSoCLinux内核调试专家介绍AMDMPSoCLinux一般使用PetaLinux编译Linux系统,包括Linux内核、DTS、文件系统。PetaLinux内部集成Yocto,自动下载、配置、编译各种软件包。它简化了编译流程,也导致有些工程师找不到软件包的源代码,不知道如何调试软件。在PetaLinux......
  • 基于Win32k内核提权漏洞的攻防对抗
    一、产生原因1.1Callback机制Win32k组件最初的设计和编写是完全建立的用户层上的,但是微软在WindowsNT4.0的改变中将Win32k.sys作为改变的一部分而引入,用以提升图形绘制性能并减少Windows应用程序的内存需求。窗口管理器(User)和图形设备接口(GDI)在极大程度上被移出客户端/......
  • shell命令概述 Shell作用:命令解释器 介于操作系统内核与用户之间,负责解释命令行 获得
    shell命令概述Shell作用:命令解释器介于操作系统内核与用户之间,负责解释命令行获得命令帮助内部命令help命令的“--help”选项使用man命令阅读手册页命令行编辑的几个辅助操作Tab键:自动补齐反斜杠“\”:强制换行快捷键Ctrl+U:清空至行首快捷键Ctrl+K:清空至行尾快捷键Ctr......
  • Mercurial(HG)版本控制服务用户名验证
    在之前的文章中,在Windows使用apache搭建Mercurial版本控制服务,并没有说到如何验证用户,而是允许所有人都提交。当时还不会怎么配置。1,用htpasswd.exe建立用户密码文件htpasswd.exe在apache的bin目录可以找到,使用方法主要有htpasswd-c文件名用户名       ......
  • Linux 内核 ASoC DMA 引擎驱动程序
    Linux内核ASoC框架,在概念上将嵌入式音频系统拆分为多个可复用的组件驱动程序,包括Codec类驱动程序、平台类驱动程序和机器类驱动程序。在实现上,机器类驱动程序用structsnd_soc_card和structsnd_soc_dai_link结构描述,属于平台类驱动程序的DMA引擎驱动程序由structsnd......
  • openeuler linux内核4.19安装(centos 同理)
    linux内核安装:安装内核步骤下载相应内核版本【我这里用的是linux-4.19.90.tar.gz】下载网址:https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/解压缩到自定位置【我这里是/root/桌面/send/】安装内核图像界面依赖【已安装则跳过】 yuminstallncurses-deve......
  • shell命令概述 Shell作用:命令解释器 介于操作系统内核与用户之间,负责解释命令行 获得
    shell命令概述Shell作用:命令解释器介于操作系统内核与用户之间,负责解释命令行获得命令帮助内部命令help命令的“--help”选项使用man命令阅读手册页命令行编辑的几个辅助操作Tab键:自动补齐反斜杠“\”:强制换行快捷键Ctrl+U:清空至行首快捷键Ctrl+K:清空至行尾快捷键Ctr......