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

news2025/1/15 16:17:16

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

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

图片

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

图片

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

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

  • 静止状态:

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

  • 宽限期:

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

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

  • 读端进入临界区

  • 读端退出临界区

  • 等待宽限期结束(同步方式)

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

图片

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 具有强烈的排他性,所以每次只能有一个读端进入临界区。

图片

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

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

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

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

图片

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

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

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

图片

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

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

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

图片

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

图片

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

  • RCU 采用延迟回收技术:

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

  • RCU 允许并发读取:

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/944653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ESDA in PySal (2) localjoincounts

ESDA in PySal (2) localjoincounts 参考:https://blog.csdn.net/angel0929/article/details/128433265 https://blog.csdn.net/allenlu2008/article/details/49895387 PySAL有5种全局自相关检验:Gamma值、Join Count、Moran’s I、Geary’s C、和Getis and Ord’s G 在下…

CVE-2023-36874 Windows错误报告服务本地权限提升漏洞分析

CVE-2023-36874 Windows错误报告服务本地权限提升漏洞分析 漏洞简介 Windows错误报告服务在提交错误报告前会创建wermgr.exe进程,而攻击者使用特殊手法欺骗系统创建伪造的wermgr.exe进程,从而以system权限执行代码。 影响版本 Windows10 1507 * Wind…

LC1011. 在 D 天内送达包裹的能力(JAVA)

在 D 天内送达包裹的能力 题目描述上期经典算法 题目描述 leetcode 1011. 在 D 天内送达包裹的能力 难度 - 中等 传送带上的包裹必须在 days 天内从一个港口运送到另一个港口。 传送带上的第 i 个包裹的重量为 weights[i]。每一天,我们都会按给出重量(we…

java主要的垃圾回收算法

垃圾收集算法了解吗? 标记-清除算法 标记 : 标记出所有需要回收的对象 清除:回收所有被标记的对象 主要存在两个缺点: 执行效率不稳定,如果 Java 堆中包含大量对象,而且其中大部分是需要被回收的,这时必…

macOS使用命令行连接Oracle(SQL*Plus)

Author: histonevonzohomail.com Date: 2023/08/25 文章目录 SQL\*Plus安装下载环境配置 SQL\*Plus远程连接数据库参考文献 原文地址:https://histonevon.top/archives/oracle-mac-sqlplus数据库安装:Docker安装Oracle数据库 (histonevon.top) SQL*Plus…

移动电源专用的单节锂离子电池充电器和恒定 5V 升压控制器HU5715

航誉微HU5715 为一款移动电源专用的单节锂离子电池充电器和恒定 5V 升压控制器,充电 部分集高精度电压和充电电流调节器、预充、充电状态指示和充电截止等功能于一体, 可以输出最大 1A 充电电流。而升压电路采用 CMOS 工艺制造的空载电流极低的 VFM 开 关…

解析经典面试题:for 循环中的 let var

题目 for循环中&#xff0c;使用 var 或 let 声明 i 变量&#xff0c;会得到不同的结果 var arr []; for (var i 0; i < 2; i) {arr[i] function () {console.log(i);} } arr[0](); arr[1]();输出&#xff1a; 2 2 var arr []; for (let i 0; i < 2; i) {arr[i] …

新兴崛起的优雅框架Mybatis-Flex

目录 介绍 基础使用 简单入门 插入数据 insertSelective insertOrUpdate insertBatch 注意事项 删除数据 ​编辑 条件删除 批量删除 注意事项 更新数据 ​编辑 条件更新 UpdateEntity使用 UpdaerWrapper使用 注意事项 查询数据 查询一条数据 selectOneById​编辑 …

深圳前海公司财政补贴

前海公司财政补贴 1.扶持领域 一般类项目和港资企业创新创业发展项目均须符合下列产业领域&#xff1a; 1.1金融业。围绕跨境人民币业务、财富管理、资产管理、股权投资、要素交易市场建设、金融产品创新、金融中介服务领域开展的有利于金融业改革创新发展的项目。 1.2现代…

视频融合平台EasyCVR视频汇聚平台关于小区高空坠物安全实施应用方案设计

近年来&#xff0c;随着我国城市化建设的推进&#xff0c;高楼大厦越来越多&#xff0c;高空坠物导致的伤害也屡见不鲜&#xff0c;严重的影响到人们的生命安全。像在日常生活中一些不起眼的小东西如烟头、鸡蛋、果核、易拉罐&#xff0c;看似伤害不大&#xff0c;但只要降落的…

架构之选:评价Spring Cloud在微服务完整性方面的独到观点!

大家好&#xff0c;我是小米&#xff01;今天我们要来聊一个非常热门的话题&#xff1a;Spring Cloud在微服务架构方面的完整度情况。随着技术的不断演进&#xff0c;微服务架构已经成为了众多企业构建灵活、可扩展系统的首选。而Spring Cloud作为微服务架构的佼佼者&#xff0…

Java 8 新特性——Lambda 表达式(2)

一、Java Stream API Java Stream函数式编程接口最初在Java 8中引入&#xff0c;并且与 lambda 一起成为Java开发里程碑式的功能特性&#xff0c;它极大的方便了开放人员处理集合类数据的效率。 Java Stream就是一个数据流经的管道&#xff0c;并且在管道中对数据进行操作&…

POI实现百万数据导出

1、概述 ​ 我们都知道Excel可以分为早期的Excel2003版本&#xff08;使用POI的HSSF对象操作&#xff09;和Excel2007版本&#xff08;使用POI的XSSF操作&#xff09;&#xff0c;两者对百万数据的支持如下&#xff1a; ​ Excel 2003&#xff1a;在POI中使用HSSF对象时&#…

Java代码通过经纬度计算省份。

直接上代码&#xff0c;需要市区县可自己解析 String areaName addressUtil.getPosition(longitude, latitude); package com.skyable.device.utils.velicle;import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import l…

如何把视频压缩到最小?

如何把视频压缩到最小&#xff1f;现在是自媒体非常流行的时代&#xff0c;视频的使用也变得非常的频繁&#xff0c;不管你是出于个人爱好还是在工作中&#xff0c;都需要拍摄和制作视频&#xff0c;因此对于视频文件的操作和处理对于我们每个人就非常的重要。随着现在手机拍摄…

基于java Swing 和 mysql实现的飞机订票系统(源码+数据库+ppt+ER图+流程图+架构说明+论文+运行视频指导)

一、项目简介 本项目是一套基于java Swing 和 mysql实现的飞机订票系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过…

20个常用Matplotlib图的Python代码,全文干货建议收藏!

今天分享给大家25个Matplotlib图的汇总&#xff0c;在数据分析和可视化中最有用。 # !pip install brewer2mpl import numpy as np import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns import warnings; warnin…

前端vue引入高德地图入门教程

距离上一篇关于前端项目中使用高德地图的文章已经将近5年之久&#xff0c; 这是我的第一篇关于高德地图的文章 这期间前端技术日新月异&#xff0c;5年前JQuery还如日中天&#xff0c;如今已经销声匿迹&#xff0c;很少有公司招聘还在要求JQuery&#xff0c;更多的是Vue、React…

Linux(扩展篇)

Linux扩展篇 软件包管理RPMRPM概述RPM查询命令RPM卸载命令RPM安装命令 YUM仓库配置YUM概述YUM的常用命令修改网络 YUM 源安装 wget, wget 用来从指定的 URL 下载文件在/etc/yum.repos.d/目录下&#xff0c;备份默认的 repos 文件下载网易 163 或者是 aliyun 的 repos 文件使用下…

大数据学习:Hive基础与DDL操作

Hive基础与DDL操作 1. 数据仓库 1.1 数据仓库的基本概念 数据仓库的英文名称为Data Warehouse&#xff0c;可简写为DW或DWH。 数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;为企业提供决策支持&#xff08;Decision Support&#xff09;。它出于分析性报告和决…