技术分享 | 编程界也内卷?浅析“斜杠青年”RCU

本文介绍了并行编程如何推动CPU发展,以Linux中的RCU机制为核心,详细阐述了其工作原理、应用场景和在Linux内核中的复杂实现。RCU通过读者无锁访问、写者复制更新等策略优化性能,为高性能和多核操作系统提供了关键技术。

随着硬件晶体管的尺寸越来越小,CPU 的频率上限已经基本保持在 4G 左右,但现代人类对于网络的要求不断升级,想要占领市场各厂商只能激发自己的潜能,将单线程代码发展为多核/多线程系统,在这其中,并行是充分利用这些系统性能的最优办法。

并行编程是一个革新式的变化,它涉及众多领域,包括硬件平台的实现方式、软件任务间的通信、任务同步等等,可以说是一个实打实的“斜杆青年”了。

今天,我们就以 Linux 中的常用 RCU 机制看看,并行编程到底是怎么被卷出来的!

图片

RCU 全称 read-copy-update ,是一种数据同步机制。

RCU 主要针对的数据对象是指针,目的是提高遍历读取数据的效率。以链表为例,为了提高效率,我们在使用 RCU 机制读取数据的时候可以不对链表进行加锁操作。这时,每当线程在共享内存中插入或删除数据结构的元素时,可以保证所有读者都看到并遍历旧结构或新结构,从而避免不一致(例如,取消引用空指针)。

图片

RCU 主要用在读取性能至关重要的场合中,为了保证所有读者的继续不受影响,并可以更快地完成实现速度,它不得不占用更多空间。这也成就了 RCU 的特点:

  • RCU 读端和更新端可以同时操作,并发执行;

  • RCU 采用数据担保的方式,为读端提供可靠数据;

  • 读端读到的数据有可能是旧的也可能是新的;

  • 宽限期结束时,读端要退出临界区;

  • 典型  RCU 读端在临界区内不允许中断、休眠等操作。

图片

图片

我们可以将 RCU 的基本执行过程分为三个步骤:

  • 采用发布——订阅机制添加新的数据

  • 等待已有的 RCU 读者退出临界区;

  • 允许在不影响或者延迟其他并发 RCU 读者的前提下改变数据。

图片

图片

q = kmalloc(sizeof(*p), GFP_KERNEL);
*q = *p;
q->b = 11;
list_replace_rcu(&p->list, &q->list);
synchronize_rcu();
kfree(p);

图片

图片

从上图我们可以看出, RCU 的基本构建思想可以归纳成:

  • 先给指针 q 分配一个空间;

  • 给指针 q 赋值;

  • 将新生成的 q 指向 p 的下一个节点;

  • 再将 p 的上一个节点指向 q ;

  • 等宽限期结束后,再将 p 释放掉。

需要注意的是,第三步和第四步的顺序不能反,否则会造成读端在读取数据的时候出现指针错误。

图片

  • 读端通过 rcu_read_lock() 进入临界区;

  • 读端通过 rcu_read_unlock() 退出临界区;

  • 更新端通过 rcu_assign_pointer() 等发布原语发布数据;

  • 更新端通过 synchronize_rcu() 同步原语等待宽限期结束。

图片

  • 读端

rcu_read_lock();
p = rcu_dereference(head);
... ...
rcu_read_unlock();

当我们进入临界区后,就可以操作获取指针 p ,并对 p 进行一系列的操作了。

  • 更新端

spin_lock(&lock);
p = head;
rcu_assign_pointer(head, NULL);
spin_unlock(&lock);
synchronize_rcu();
kfree(p);

该段代码先通过 rcu_assign_pointer() 函数将 head 置为 NULL ,然后通过 synchronize_rcu() 等待宽限期结束。当 synchronize_rcu() 函数返回时,表示宽限期结束,所有的读端都已经退出临界区,此时就可以释放 p(原来head) 指向的空间。

图片

图片

图片

图片

图片

当我们了解了 RCU 的全部执行过程时,就可以将其基本思想概括为三类:

  • 读者无锁访问数据,标记进出临界区;

  • 写者读取,复制,更新;

  • 旧数据延迟回收。

说起来容易,但其在 Linux 内核中实现却不是这么简单。除了要实现基本功能,还需要考虑很多复杂情况。内核的 RCU 系统可以说是内核最复杂系统之一,为了高性能和多核扩展性,它被设计了非常精巧的数据结构,还结合了预处理,批量处理,延后(异步)处理,多核并发,原子操作,异常处理,多场景精细优化等多种技术,也体现很多编程思想和代码设计思想,有一定的学习和参考价值。

想要打造一款以人为本,为用户提供更便捷、智能、安全的操作系统,我们保持着对各类新兴技术的关注与热情,RCU 具备的性能好,可扩展性强,稳定性强等特点,正符合鼎道智联想要打造 DingOS 的目标。合理运用 RCU 技术,就可以提高用户的响应速度、提升系统的稳定性和可靠性,从而给用户带来更安全、流畅的操作体验。如果你也认可我们的想法,有相同的目标,欢迎加入或关注鼎道生态。

内容概要:本文围绕六自由度机械臂的人工神经网络(ANN)设计展开,重点研究了正向与逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程,并通过Matlab代码实现相关算法。文章结合理论推导与仿真实践,利用人工神经网络对复杂的非线性关系进行建模与逼近,提升机械臂运动控制的精度与效率。同时涵盖了路径规划中的RRT算法与B样条优化方法,形成从运动学到动力学再到轨迹优化的完整技术链条。; 适合人群:具备一定机器人学、自动控制理论基础,熟悉Matlab编程,从事智能控制、机器人控制、运动学六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)建模等相关方向的研究生、科研人员及工程技术人员。; 使用场景及目标:①掌握机械臂正/逆运动学的数学建模与ANN求解方法;②理解拉格朗日-欧拉法在动力学建模中的应用;③实现基于神经网络的动力学补偿与高精度轨迹跟踪控制;④结合RRT与B样条完成平滑路径规划与优化。; 阅读建议:建议读者结合Matlab代码动手实践,先从运动学建模入手,逐步深入动力学分析与神经网络训练,注重理论推导与仿真实验的结合,以充分理解机械臂控制系统的设计流程与优化策略。
<think> 我们讨论的是嵌入式系统中的RCU(复位与时钟控制)寄存器配置,以调用内部晶振(如STM32中的HSI)。以下是配置步骤: 1. **选择HSI作为系统时钟源**: - 将RCU控制寄存器(如RCC_CR)中的HSION位置1,使能HSI振荡器。 - 等待HSIRDY标志置位,确认HSI就绪。 2. **配置系统时钟切换**: - 在RCU时钟配置寄存器(如RCC_CFGR)中,设置SW[1:0]位为00(或对应值),选择HSI作为系统时钟源。 - 等待SWS[1:0]位变为00,确认切换完成。 3. **关闭其他时钟源(可选)**: - 若之前使用HSE或PLL,需关闭它们以节能: - 清除RCC_CR中的HSEON位。 - 清除RCC_CR中的PLLON位。 4. **调整分频器(可选)**: - HSI通常固定为8MHz(不同芯片可能不同),若需降频,可通过RCC_CFGR的分频位(如HPRE)配置。 示例代码(STM32F4系列): ```c // 1. 使能HSI RCC->CR |= RCC_CR_HSION; // 开启HSI while (!(RCC->CR & RCC_CR_HSIRDY)); // 等待HSI就绪 // 2. 切换系统时钟到HSI RCC->CFGR &= ~RCC_CFGR_SW; // 清除SW位(默认为00,即HSI) RCC->CFGR |= RCC_CFGR_SW_HSI; // 设置SW=00(HSI),某些芯片需显式写00 while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); // 等待切换成功 // 3. 关闭HSE和PLL(若之前开启) RCC->CR &= ~RCC_CR_HSEON; // 关闭HSE RCC->CR &= ~RCC_CR_PLLON; // 关闭PLL ``` **注意事项**: - HSI精度较低(±1%),适用于对时钟精度要求不高的场景。 - 切换时钟源期间,CPU会暂停运行直至稳定。 §§相关问题§§ 1. 如何通过RCU寄存器将系统时钟从HSI切换到HSE(外部晶振)? 2. 在切换时钟源时,为什么需要等待标志位(如HSIRDY、SWS)?如果跳过等待会有什么风险? 3. 如何通过RCU寄存器校准内部HSI时钟以提高精度? 4. 如果系统同时使用多个时钟源(如HSI和LSI),RCU如何管理其优先级和切换? </think> ### 通过 RCU 寄存器配置内部晶振(以 STM32 的 HSI 为例) #### 核心步骤: 1. **使能 HSI 振荡器** 在 RCU 控制寄存器 (`RCC_CR`) 中设置 `HSION` 位: ```c RCC_CR |= (1 << 0); // 开启 HSI 振荡器 while (!(RCC_CR & (1 << 1))); // 等待 HSIRDY 标志置位(就绪) ``` 2. **切换系统时钟源** 在 RCU 时钟配置寄存器 (`RCC_CFGR`) 中选择 HSI: ```c RCC_CFGR &= ~(0x3 << 0); // 清除 SW[1:0] 位 RCC_CFGR |= (0x0 << 0); // SW=00 (选择 HSI) while ((RCC_CFGR & (0x3 << 2)) != 0x0); // 等待 SWS[1:0]=00 (切换完成) ``` 3. **关闭其他时钟源(可选)** 禁用不必要的外部振荡器(HSE)以节能: ```c RCC_CR &= ~(1 << 16); // 关闭 HSE ``` #### 完整配置代码(STM32F4 示例): ```c void SwitchToHSI(void) { // 1. 使能 HSI RCC->CR |= RCC_CR_HSION; // 开启 HSI while (!(RCC->CR & RCC_CR_HSIRDY)); // 等待就绪 // 2. 切换到 HSI 作为系统时钟 RCC->CFGR &= ~RCC_CFGR_SW; // 清除时钟源选择位 RCC->CFGR |= RCC_CFGR_SW_HSI; // 选择 HSI (SW=00) while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); // 确认切换 // 3. 关闭 HSE (可选) RCC->CR &= ~RCC_CR_HSEON; // 禁用外部晶振 } ``` #### 关键注意事项: 1. **时钟频率** HSI 默认频率通常为 8/16MHz(依芯片型号而定),需通过 `RCC_CFGR` 的分频器(如 `HPRE`)调整 2. **振荡器校准** 若需提高精度,通过 `RCC_CR` 的 `HSITRIM[4:0]` 位校准(需参考芯片手册) 3. **中断处理** 切换时钟时可能触发时钟安全中断(CSS),需在中断服务程序中处理 > ⚠️ **警告**:操作前需关闭所有依赖高精度时钟的外设(如 USB、SDIO)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鼎道开发者联盟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值