前言
最近报名参加了恩智浦社区的 lpc55s69 开发板测评活动,由于其搭载的是一颗 cortex-m33 dual core 的 cpu,而且有大佬已经支持了 rt-thread 的 bsp,就考虑使其支持 rt-thread 框架下的 smp,最近就一直在研究 smp,并在 raspberry-pico 上做了一些实验。以下是一些我在学习过程中的心得体会,不对的地方欢迎大家指正交流~
smp 简介
smp: 对称多处理(symmetrical multi-processing)简称 smp,是指在一个计算机上汇集了一组处理器 (多 cpu), 各 cpu 之间共享内存子系统以及总线结构。虽然同时使用多个cpu,但是从管理的角度来看,它们的表现就像一台单机一样。系统将任务队列对称地分布于多个cpu之上,从而极大地提高了整个系统的数据处理能力。rt-thread 自 v4.0.0 版本开始支持 smp,在对称多核上可以通过使能 rt_using_smp 来开启。
系统上电后,各 cpu 的启动流程如下图所示:
每个次级 cpu 自身硬件部分的初始化不能由 cpu0 完成,因为其自身硬件不能由其它 cpu 访问。
关于 cpu 已经支持了 smp 的平台
rt-thread 的 libcpu 中有一些芯片类型已经支持了 smp 功能,例如 cortex-a 系列。对于这样的平台,smp 的移植工作就会简单很多,我们只需要实现 rt_hw_secondary_cpu_up() ,secondary_cpu_c_start() ,rt_hw_secondary_cpu_idle_exec() 这三个函数即可,具体的移植介绍可以参考 rt-thread 文档中心smp 介绍与移植
关于 cpu 还未支持 smp 的平台
rt-thread 中还有一些 cpu 是没有支持 smp 的,例如 cortex-m 系列的大部分 cpu,练手的 pico 是 m0 ,准备开发的 lpc55s69 是 m33,都是还没有支持 smp 的。对于这样的平台移植 smp 就会相对麻烦。除了 rt_hw_secondary_cpu_up() ,secondary_cpu_c_start() ,rt_hw_secondary_cpu_idle_exec() 这三个函数,我们还需要补充 smp 所需要的底层支持,主要是中断和调度部分,从而实现更加复杂的共享资源保护,以及线程间的通讯和调度。
推荐大家先去看看 rt-thread 文档中心的相关资料,以及这篇文章:[rt-thread学习笔记] 中断锁、调度锁与死锁,最好再去看看 rt-threadsrc 目录下的 scheduler.c 源码。其中有许多 smp 的相关实现。
cpu id
scheduler.c 中已经有 smp 的相关支持,但是会发现,还需要 cpu 的个数及其对应的 id 等重要参数。所以我们首先要是实现的是 rt_hw_cpu_id() 这个函数。这个需要对应自己开发平台的实际情况。
os tick
在 smp 系统中,每个 cpu 维护自己独立的 tick 值,用作任务运行计时以及时间片统计。除此之外,cpu0 还通过 tick 计数来更新系统时间,并提供系统定时器的功能,次级 cpu 不需要提供这些功能。这部分也是要针对使用的开发开发平台进行配置。
处理器间中断 ipi
处理器间中断(inter-processor interrupt)负责 cpu 之间的相互通讯及处理,针对不同开发的平台,我们还需要实现以下函数:
/* 该函数用来向 cpu 位图中表示的 cpu 集合发送指定编号的 ipi 信号 /
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
/ 函数为当前 cpu 设置指定编号 ipi 信号的处理函数 */
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler)
调度与同步
临界区保护是需要特别注意的。对于 smp 通过关中断的方式并不能阻止多个 cpu 对共享资源的并发访问,需要通过自旋锁机制进行保护(在次级 cpu 启动中就需要使用)。我们就还需要实现以下几个函数:
rt_hw_spin_lock_init() /* 初始化已分配的 spinlock 变量 /
rt_hw_spin_lock() / 获取 spinlock,忙等待直到获取成功 /
rt_hw_spin_unlock() / 释放 spinlock */
需要定义自旋锁:
typedef union {
unsigned long slock;
struct __arch_tickets {
unsigned short owner;
unsigned short next;
} tickets;
} rt_hw_spinlock_t;
不使用 smp 的时候,在进行调度等需要临界资源保护的情况下,是通过 rt_hw_interrupt_disable/enable 来屏蔽中断进行保护,但是在 smp 中,对 rt_hw_interrupt_disable/enable 进行了替换,从而保证对共享资源访问的互斥:
/* 在 rthw.h 中 */
#ifdef rt_using_smp
#define rt_hw_interrupt_disable rt_hw_local_irq_disable
#define rt_hw_interrupt_enable rt_hw_local_irq_enable
#endif
因为 smp 使用了多核,调度和同步的情况相较于单个处理器更加复杂和重要,这部分是移植的重点。需要针对开发平台的不同,对以下函数进行重写(对于 cortex-m 内核,需要注意辅助上下文切换的 pendsv 也是一种中断):
/* 实现从当前线程切换到目标线程,在 cortex-m 内核里 rt_hw_context_switch() 和 rt_hw_context_switch_interrupt() 功能一致 /
rt_hw_context_switch_interrupt:
rt_hw_context_switch:
/ 实现没有来源线程切换到目标线程 /
rt_hw_context_switch_to:
/ pendsv 中断处理函数,在 cortex-m 内核里 pendsv 辅助完成上下文切换 */
pendsv_handler:
这部分需要在 context_gcc.s 文件中实现。
移植 smp 的重点是调度与同步,大家在移植之前,最好对 rt-thread 的调度流程和中断机制有一定的学习和理解,这部分可以参考 rt-thread 文档中心,最好能配合着理解源码的实现。我对 rt-thread 框架下的 smp 目前的理解就是以上这些,欢迎大家交流讨论。
小米MIUI系统3大升级确认
三相鼠笼式异步电动机的点动和自锁控制线路
桌面千兆像素显微镜的制作
TFT-LCD显示屏工作原理
漏电保护器跳闸原因及排查方法
RT-Thread框架下的SMP支持
一加7Pro拆机图解
胡厚崑与14家顶尖外媒座谈实录:对于安全指控,最好是让事实说话
双向最小齐纳二极管概述
恒坤光电的光学研发团队推出了LED汽车双光大灯方案,中国制造再获突破
智能风电系统是提升海上风电运维可靠性的数字抓手
The Freestyle三星投影仪:强大的投影功能和高清画质技术
毫米波人体成像设备将取代金属探测门用于民用机场
纳智捷锐3 旗舰版全方位测评
苹果正在研发折叠屏iPhone可能会在5G手机之后推出
详解卷积神经网络卷积过程
基于Linux2.6的YAFFS文件系统移植
人类历史上推力最大的火箭,在发射前几分钟突然被叫停!
传新版Model3即将上市,特斯拉在国内促销清库存?
ARM微处理器对异常中断的响应过程