RT-Thread SMP介绍与移植

2024-01-13 09:52
文章标签 介绍 移植 thread rt smp

本文主要是介绍RT-Thread SMP介绍与移植,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SMP:(Symmetrical Multi-Processing)对称多处理,简称SMP,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构。

RT-Thread自v4.0.0版本开始支持SMP,在对称多核上可以通过使能RT_USING_SMP来开启。

多核启动

系统上电后,每个CPU都会在ROM中的代码控制下独自运行,但是只有主处理器(简称CPU0)跳转到RT-Thread的初始化入口处,而其它的处理器(简称次级CPU)则会暂停在某个状态下,等待CPU0将它们唤醒

CPU0完成RT-Thread的全局初始化过程,包括外设初始化,中断控制器的中断分发部分初始化、全局变量的初始化、全局内核对象的创建等等。

最终,CPU0在执行main线程之前,唤醒其它的次级CPU,引导它们执行次级CPU的初始化代码,这段代码会让各个次级CPU去完成自身相关的硬件初始化,并开启任务调度。

此后,系统进入正常运行阶段。系统启动阶段各个 CPU 的动作如下图所示:
在这里插入图片描述
每个次级CPU自身硬件部分的初始化不能由CPU0完成,因为其自身硬件不能由其它CPU访问

CPU0启动流程

在SMP平台上,启动核心CPU0的启动流程和单核CPU上的启动过程相同,主要流程如图所示:
在这里插入图片描述
由于硬件平台和编译器的不同,系统上电后执行的初始化代码和启动流程并不相同,但是系统最终统一调用入口函数rtthread_startup()来启动RT-Thread。
该函数设置硬件平台、初始化操作系统各组件、创建用户main线程,并最终开启当前CPU的任务调度机制,开始正常工作。

当开启了CPU0的任务调度器之后,CPU0上通常存在两个线程:main线程和idle线程,用户可以通过修改配置选项或者通过RT-Thread提供的接口创建新的线程,调度器依据优先级和线程状态从中选择就绪线程执行。

次级CPU启动流程

如果定义了配置选项RT_USING_SMP,CPU0的main线程在运行过程中会执行函数rt_hw_secondary_cpu_up()以启动其它CPU核心。

该函数由移植内核的开发人员提供,完成以下两个操作:

  1. 设置次级CPU的启动入口地址;
  2. 加电启动次级CPU。

在RAMv7-A中,次级CPU的启动入口地址固定设置为secondary_cpu_start,该标号定义在文件libcpu/arm/cortex-a/start_gcc.S中,主要包括设置当前CPU的内核栈,建立MMU内存映射表,然后跳转到函数secondary_cpu_c_start()执行。

函数secondary_cpu_c_start()是所有次级CPU的初始化函数,它同样与硬件平台密切相关,由移植系统的开发者提供,需要完成以下步骤:

  1. 初始化当前CPU的中断控制器,设置中断向量表;
  2. 设置定时器为当前CPU产生tick中断;
  3. 获取内核自旋锁_cpus_lock以保护全局任务表,调用函数rt_system_scheduler_start()开启当前CPU的任务调度器。

全志T3芯片采用GIC中断控制器,系统通过Generic Timer定时器提供tick中断计数,函数secondary_cpu_c_start()代码如下:

在这里插入图片描述
每个次级CPU启动之后,从全局任务表和当前CPU的局部任务表中选取优先级最高的任务执行,在优先级相同的情况下,优先选择当前CPU的局部任务表中的任务执行。

在不存在其它任务的情况下,每个CPU调度自己的idle任务执行。
其中,CPU0的idle任务循环执行函数rt_thread_idle_execute(),而次级CPU的idle任务循环执行函数rt_hw_secondary_cpu_idle_exec()。
在这里插入图片描述

任务特性

RT-Thread中的任务分为以下状态:

  • 运行态:任务正在某个CPU上执行;
  • 就绪态:任务随时可以被执行,但尚未分配到CPU,因此等待在某个就绪态任务表中;
  • 挂起态:任务因条件不满足(等待超时或者数据到来等),而不能够被执行。
  • 关闭态:任务已经被删除,正在等待被回收。

在进入正常运行阶段后,每个CPU都独自地运行中断处理、调度器以及任务的代码。RT-Thread在多核系统上运行时存在以下特性:

  1. 同一时刻,一个任务(线程)只会运行在一个CPU上;
  2. 每个CPU互斥地访问全局调度器数据,以确定将要在当前CPU上运行的任务;
  3. 支持将任务绑定在某一个CPU上运行。

调度策略

为了实现上述目标,RT-Thread调度器实现了两种就绪任务队列:

  1. 全局就绪任务表rt_thread_ready_table[],包含没有绑定CPU的就绪任务;
  2. CPU局部就绪任务表ready_table[],每个CPU对应一个,包含绑定到对应CPU的就绪任务。典型的CPU绑定任务是每个CPU自己的idle任务。

当CPU需要切换任务执行时,任务调度器查找系统中优先级最高的就绪任务执行,即全局就绪任务表和当前CPU的局部就绪任务表中优先级最高的任务。在优先级相同的情况下,优先选取局部任务表中的任务。

相对应的是,如果一个任务由其它状态变为就绪状态,则进行如下处理:

  1. 如果它不是CPU绑定任务,则把它挂入全局就绪表,并向其它的所有CPU发送IPI中断,通知它们检查是否需要切换任务,因为其它CPU的当前任务的优先级可能低于此就绪态任务,因而会发生优先级抢占。
  2. 如果它是一个CPU绑定任务,检查它是否比对应CPU的当前任务优先级高,如果是则发生优先级抢占,否则把它挂入对应CPU的局部就绪任务表。整个过程不通知其它CPU。

处理器间中断IPI

当单个CPU上运行的任务改变了系统状态,或者触发了某个事件,需要通过处理器间中断(Inter-Processor Interrupt)通知其它CPU,其它CPU在收到该信号后,调用注册的相应例程进行处理。

void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)

OS Tick

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

在初始化次级 CPU 的过程中,每个 CPU 需要使能各自的 tick 定时器,并注册相应的 tick 中断处理函数。使能 tick 定时器的操作与具体的硬件平台相关,需要移植内核的开发者提供;而 tick 中断处理函数主要完成两个动作:

设置 tick 定时器的状态。
增加当前 CPU 的 tick 计数。

自旋锁spinlock

在SMP系统中,通过关中断的方式不能阻止多个CPU对共享资源的并发访问,需要通过自旋锁机制进行保护。
和互斥锁类似,在任何时刻,自旋锁最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。
不同的是,对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。而自旋锁不会引起调用者睡眠,而是循环查询直到该自旋锁的保持者已经释放了锁。

初始化已分配的spinlock变量:

void rt_spin_lock_init(struct rt_spinlock *lock)

获取spinlock,忙等待直到获取成功:

void rt_spin_lock(struct rt_spinlock *lock)

释放spinlock:

void rt_spin_unlock(struct rt_spinlock *lock)

如下函数禁止当前CPU中断后获取spinlock,忙等待直到获取成功:

rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock)

任务绑定

通常系统中的就绪任务位于全局就绪任务表中,每个任务在哪个CPU上调度运行是随机的。
通过将就绪任务放入到某个CPU局部就绪任务表列表中,RT-Thread允许将任务与CPU绑定,即该任务只能够在指定的CPU上。

rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *args)

当参数cmd的值为RT_THREAD_CTRL_BIND_CPU时,函数将线程thread绑定到参数arg指定的CPU上。

这篇关于RT-Thread SMP介绍与移植的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/601068

相关文章

Python中win32包的安装及常见用途介绍

《Python中win32包的安装及常见用途介绍》在Windows环境下,PythonWin32模块通常随Python安装包一起安装,:本文主要介绍Python中win32包的安装及常见用途的相关... 目录前言主要组件安装方法常见用途1. 操作Windows注册表2. 操作Windows服务3. 窗口操作

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h

MybatisPlus service接口功能介绍

《MybatisPlusservice接口功能介绍》:本文主要介绍MybatisPlusservice接口功能介绍,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录Service接口基本用法进阶用法总结:Lambda方法Service接口基本用法MyBATisP

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

Pytorch介绍与安装过程

《Pytorch介绍与安装过程》PyTorch因其直观的设计、卓越的灵活性以及强大的动态计算图功能,迅速在学术界和工业界获得了广泛认可,成为当前深度学习研究和开发的主流工具之一,本文给大家介绍Pyto... 目录1、Pytorch介绍1.1、核心理念1.2、核心组件与功能1.3、适用场景与优势总结1.4、优

Java实现本地缓存的常用方案介绍

《Java实现本地缓存的常用方案介绍》本地缓存的代表技术主要有HashMap,GuavaCache,Caffeine和Encahche,这篇文章主要来和大家聊聊java利用这些技术分别实现本地缓存的方... 目录本地缓存实现方式HashMapConcurrentHashMapGuava CacheCaffe

Spring Security介绍及配置实现代码

《SpringSecurity介绍及配置实现代码》SpringSecurity是一个功能强大的Java安全框架,它提供了全面的安全认证(Authentication)和授权(Authorizatio... 目录简介Spring Security配置配置实现代码简介Spring Security是一个功能强

JSR-107缓存规范介绍

《JSR-107缓存规范介绍》JSR是JavaSpecificationRequests的缩写,意思是Java规范提案,下面给大家介绍JSR-107缓存规范的相关知识,感兴趣的朋友一起看看吧... 目录1.什么是jsR-1072.应用调用缓存图示3.JSR-107规范使用4.Spring 缓存机制缓存是每一