当前位置: 首页 > news >正文

RT-Thread SMP相关问题分析

问题分析:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/smp/smp?id=os-tick

OS Tick
在 SMP 系统中,每个 CPU 维护自己独立的 tick 值,用作任务运行计时以及时间片统计。除此之外,CPU0 还通过 tick 计数来更新系统时间,并提供系统定时器的功能,次级 CPU 不需要提供这些功能。

SMP 系统中 CPU0 和次级 CPU 的 OS Tick 管理差异分析

在 RT-Thread SMP (对称多处理) 系统中,尽管每个 CPU 都维护自己独立的 tick 值用于任务运行计时和时间片统计,但只有 CPU0 负责更新系统时间和提供系统定时器功能,次级 CPU 不需要提供这些功能。这种设计有其深层的技术考量。

次级 CPU 不提供系统时间更新和系统定时器功能的原因

1. 避免系统时间不一致性

时间同步问题

  • 如果多个 CPU 同时更新系统时间,会导致时间跳变或回退
  • 不同 CPU 的硬件计时器可能存在微小差异,长时间运行会导致偏差
  • 由单一 CPU (CPU0) 负责时间维护确保了系统时间的单调递增性

2. 避免资源竞争和提高效率

锁竞争减少

  • 系统时间是全局共享资源,多 CPU 访问需要加锁保护
  • 只由 CPU0 负责更新可以避免频繁的锁操作和竞争
  • 减少了跨 CPU 的缓存同步开销,提高系统效率

3. 简化系统设计与实现

架构简洁性

  • 集中式时间管理模型设计更简单清晰
  • 降低了系统实现的复杂度
  • 减少了潜在的时序错误和竞态条件

4. 系统定时器任务的统一调度

定时任务处理

  • 系统定时器通常用于周期性任务调度
  • 单一 CPU 处理定时事件避免了重复触发和执行
  • 确保定时任务只执行一次,避免重复执行带来的资源浪费

5. 中断处理效率

中断处理优化

  • 时钟中断是高频触发的中断源
  • 仅在 CPU0 处理系统级时钟中断可减少整体中断负载
  • 次级 CPU 可以专注于处理其他任务和中断

RT-Thread SMP 中的实际实现

在 RT-Thread SMP 实现中,所有 CPU 都有各自的 tick 中断和计数器,用于:

  1. 记录当前 CPU 上运行的任务的时间片使用情况
  2. 在需要时触发任务切换
  3. 维护本 CPU 的调度和时间相关统计

但是只有 CPU0 会:

  1. 调用 rt_tick_increase() 更新全局系统时钟
  2. 检查并触发系统定时器列表中到期的定时器
  3. 执行系统级的周期性任务

这种设计在保证每个 CPU 都能独立进行任务调度的同时,又确保了系统时间和定时器的一致性和准确性。

结论

次级 CPU 不提供系统时间更新和系统定时器功能,是 RT-Thread SMP 系统设计中为了提高性能、简化实现、确保时间一致性而采取的策略。这样的设计既保证了系统正确性,又提高了执行效率,是 SMP 操作系统设计中常见且合理的架构选择。

问题分析:
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/smp/smp?id=%e5%ae%9e%e7%8e%b0%e6%ac%a1%e7%ba%a7-cpu-%e7%9a%84%e5%90%af%e5%8a%a8%e4%bb%a3%e7%a0%81

按照上面次级 CPU 启动过程的描述,在将 RT-Thread 移植到其它 ARMv7-A SMP 芯片的过程中,内核开发者需要提供以下三个函数:

rt_hw_secondary_cpu_up(), 该函数设置次级 CPU 的启动入口地址为 secondary_cpu_start ,加电启动其它 CPU 核心;

secondary_cpu_c_start(), 该函数用来初始化单个次级 CPU ,主要包括初始化中断控制器接口,设置中断向量表,以及当前 CPU 的 tick 中断。最后获取内核自旋锁 _cpus_lock ,并调用函数 rt_system_scheduler_start() 开启当前 CPU 的任务调度器;

rt_hw_secondary_cpu_idle_exec(), 该函数被次级 CPU 的 idle 线程循环调用,可用来做功耗相关的处理。

次级 CPU 启动代码中获取内核自旋锁 _cpus_lock 的目的分析

在 SMP (对称多处理器) 系统中,次级 CPU 启动过程中的 secondary_cpu_c_start() 函数最后需要获取内核自旋锁 _cpus_lock,这是 RT-Thread SMP 系统设计中的一个关键环节。下面详细分析这么做的目的和重要性。

_cpus_lock 自旋锁的作用

_cpus_lock 是 RT-Thread SMP 内核中一个核心的自旋锁,它在系统启动和 CPU 同步过程中扮演着关键角色。在次级 CPU 启动过程中获取该锁的主要目的包括:

1. 确保 CPU 初始化的顺序性和同步性

在 SMP 系统启动过程中,CPU0 (主 CPU) 首先初始化系统的基础设施,包括内存管理、线程调度器等核心组件。次级 CPU 必须等待这些初始化完成后才能开始自己的调度。_cpus_lock 锁保证了这种顺序性:

  • CPU0 首先持有 _cpus_lock
  • 完成系统初始化后释放该锁
  • 次级 CPU 必须获取该锁才能继续执行,确保它们在系统完全就绪后才开始任务调度

2. 防止次级 CPU 过早参与系统调度

如果次级 CPU 在系统核心组件初始化完成之前就开始任务调度,可能会导致以下问题:

  • 访问未初始化完成的数据结构
  • 与 CPU0 的初始化过程产生竞争条件
  • 导致系统状态不一致

通过 _cpus_lock 机制,次级 CPU 会在自旋等待直到 CPU0 完成必要的初始化工作并释放锁,从而确保系统状态的一致性。

3. 作为启动信号机制 (Barrier)

_cpus_lock 实际上充当了一个启动信号或同步屏障 (barrier):

  • CPU0 释放锁相当于发出"系统已准备就绪"的信号
  • 次级 CPU 等待并获取锁相当于接收到该信号,表示"可以开始任务调度"

总结

次级 CPU 在启动过程中获取内核自旋锁 _cpus_lock 的主要目的是:

  1. 同步启动顺序:确保 CPU0 完成系统核心组件的初始化后,次级 CPU 才开始任务调度
  2. 保证系统状态一致性:防止次级 CPU 在系统未完全初始化时访问核心数据结构
  3. 作为启动屏障:提供一个清晰的同步点,表示"系统已准备就绪,次级 CPU 可以开始工作"

处理器间中断(Inter-Processor Interrupt,IPI):https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/smp/smp?id=%e5%a4%84%e7%90%86%e5%99%a8%e9%97%b4%e4%b8%ad%e6%96%ad-ipi

RT-Thread 处理器间中断(IPI)机制分析

处理器间中断(Inter-Processor Interrupt,IPI)是多核系统中一个核心通知其他核心发生特定事件的机制。在RT-Thread操作系统中,当某个CPU上运行的任务改变了系统状态或触发了需要其他CPU感知的事件时,就会使用IPI机制通知其他CPU。

IPI实现逻辑

1. 硬件层实现

从代码中可以看到,RT-Thread的IPI机制基于RISC-V架构的CLINT (Core Local Interruptor)硬件实现:

void clint_ipi_send(uint64_t id)
{CLINT_Type *clint = (CLINT_Type *)CORET_BASE;#if defined(CONFIG_RISCV_SMODE) && CONFIG_RISCV_SMODE// S模式下使用SSIP寄存器switch (id){case C907_CORE0:clint->SSIP0 |= (uint32_t)0x1;break;// 其他核心类似...}
#else// M模式下使用MSIP寄存器switch (id){case C907_CORE0:clint->MSIP0 |= (uint32_t)0x1;break;// 其他核心类似...}
#endif
}

这个函数通过设置CLINT中对应核心的软件中断挂起位(MSIP/SSIP)来触发目标核心的软件中断。

2. 软件层封装

RT-Thread进一步封装了硬件IPI机制,提供了更高级的API:

void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
{int idx;for (idx = 0; idx < RT_CPUS_NR; idx++){if (cpu_mask & (1 << idx)){clint_ipi_send(idx);}}
}

这个函数通过位掩码cpu_mask指定要发送IPI的目标CPU,对每个设置了相应位的CPU调用clint_ipi_send。参数ipi_vector用于指定IPI的类型,不同类型的IPI会触发不同的处理函数。

IPI使用逻辑

从RT-Thread内核代码中,我们可以看到IPI在调度器中的典型使用场景:

1. 在线程调度中的应用

_sched_insert_thread_locked函数中,当一个线程被插入就绪队列时,需要通知可能的目标CPU进行调度:

if (bind_cpu == RT_CPUS_NR)  // 线程没有绑定特定CPU
{// 将线程添加到全局就绪队列// ...// 通知当前CPU以外的所有CPU进行调度cpu_mask = RT_CPU_MASK ^ (1 << cpu_id);rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
}
else  // 线程绑定到特定CPU
{// 将线程添加到绑定CPU的就绪队列// ...// 如果当前CPU不是绑定的CPU,则通知绑定的CPU进行调度if (cpu_id != bind_cpu){cpu_mask = 1 << bind_cpu;rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);}
}

2. IPI的使用模式

从代码分析,RT-Thread中IPI的使用模式主要包括:

  1. 调度触发:当线程状态变化时(如从阻塞到就绪),需要通知可能运行该线程的CPU进行重新调度。

    • 使用RT_SCHEDULE_IPI类型的IPI。
  2. CPU间同步:当多核共享资源发生变化时,需要通知其他CPU更新其缓存或状态。

  3. 负载均衡:在负载均衡场景下,可能需要触发其他CPU重新分配任务。

3. IPI处理流程

当CPU接收到IPI后:

  1. 硬件触发软件中断
  2. 软件中断处理程序根据IPI类型执行相应的处理函数
  3. 对于RT_SCHEDULE_IPI类型,会调用rt_schedule进行任务调度重新评估

总结

RT-Thread的IPI机制提供了多核系统中CPU间通信的重要手段:

  1. 基于硬件CLINT实现低层次的处理器间中断
  2. 提供软件层次的封装,使IPI使用更加灵活
  3. 在任务调度、共享资源管理等场景中,使用IPI保证多核系统的状态一致性和及时响应

这种机制确保了当一个CPU上的任务改变了系统状态时,其他相关CPU能够及时得到通知并作出相应处理,是多核系统正常运行的关键。


文章转载自:

http://UvLQmRdk.zbtfz.cn
http://xxzdfteJ.zbtfz.cn
http://eeiiuVZF.zbtfz.cn
http://66SaAyAl.zbtfz.cn
http://0Taqymrb.zbtfz.cn
http://BRoYpbCq.zbtfz.cn
http://tZxc5d3T.zbtfz.cn
http://noYAO1nY.zbtfz.cn
http://8ZwkD6ri.zbtfz.cn
http://W2FZkvW1.zbtfz.cn
http://dlSYrAWr.zbtfz.cn
http://2Ndc27zM.zbtfz.cn
http://W2UyedRW.zbtfz.cn
http://ZsRDjgXg.zbtfz.cn
http://jQgzeK9Z.zbtfz.cn
http://v3OBOOwz.zbtfz.cn
http://bCJ26TbO.zbtfz.cn
http://DvARd5LV.zbtfz.cn
http://5ICb6Knc.zbtfz.cn
http://wWVVe4Uf.zbtfz.cn
http://krgb0kt2.zbtfz.cn
http://ct3wP55B.zbtfz.cn
http://3KwZyxuM.zbtfz.cn
http://kOvO1T2o.zbtfz.cn
http://EJjcCzVp.zbtfz.cn
http://oghhmOyk.zbtfz.cn
http://Vc24RZAv.zbtfz.cn
http://ioodDjIz.zbtfz.cn
http://fHaQGBrP.zbtfz.cn
http://Lyugvs8w.zbtfz.cn
http://www.dtcms.com/a/362670.html

相关文章:

  • 01-html css
  • 【论文阅读】Jet-Nemotron: 高效语言模型与后神经网络架构搜索
  • 11.《简单的路由重分布基础知识探秘》
  • 解决完美主义的方法是,去追求不完美--辩证法
  • 《Stable Diffusion XL 1.0 实战:AI 绘画从 “能看” 到 “好看” 的升级技巧》
  • Android把源Bitmap中心缩放到固定宽高的尺寸,Kotlin
  • Kaia AMA 全回顾:如何让 Web3 无痕融入2.5 亿用户日常?9 月 7 日中国行揭秘!
  • WPF启动窗体的三种方式
  • 达梦:存储过程实现多个用户之间表的授权
  • 如何在本地环境中搭建 GitLab 服务器
  • 《IC验证必看|SV中Process控制》
  • ffmpeg 安装
  • 添加⽂件--场景⼆
  • JVM1.8与1.9的区别是什么?
  • 实验2-代理模式和观察者模式设计
  • 实验1-工厂方法和抽象工厂模式
  • C++编程语言:标准库:第37章——正则表达式(Bjarne Stroustrup)
  • 支付系统设计模式应用:从单例到观察者模式实践
  • 普通大学生的 Web3 实习怎么找?行业指南与实践技巧这里看
  • ArkUI核心功能组件使用(一)
  • ChatDOC工具测评:AI驱动PDF/Word文档处理,支持敏感内容隐私保护与表格提取分析
  • 一文吃透 deviceQuery:从安装到输出解读,彻底验证服务器 GPU 环境
  • Elasticsearch 核心知识与常见问题解析
  • 【学Python自动化】 7.1 Python 与 Rust 输入输出对比学习笔记
  • Dell 服务器更新Infiniband网卡固件操作
  • 大模型适配国产化服务器昇腾(300I DUO)
  • 信创服务器总死机原因及解决办法
  • 通过 FinalShell 访问服务器并运行 GUI 程序,提示 “Cannot connect to X server“ 的解决方法
  • 【技术教程】如何将文档编辑器集成至基于Node.js的网页应用程序中
  • Babylon 编辑器快捷键小记