【需求价值】:
支持多核SMP
【需求描述】:
支持多核SMP
【方案描述】:
【配套文档】:
【验收标准】:
1、实现多核基础功能:
1)多核启动框架;
2)多核调度;
3)绑核操作;
2、支持多核功能裁剪,即单核运行或多核运行;
3、保证多核运行尽量独立,减少竞态,提高并行能力;
4、多核性能测试工具,输出多核性能提升数据;
5、支持spinlock死锁检测机制;
6、输出需求分析、架构分析、详细设计、测试用例、开发指南、维测手段等文档;
【技能要求】:
1、熟悉内核调度模块;
2、熟悉架构多核启动流程;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
liteos_m适配smp,主要需要修改的地方为arch、los_task、los_sched
arch需要新增接口用于多核适配
los_task模块需要将任务调度时用到的全局变量私有化
新增los_precpu,管理每个核心私有的属性
多核启动部分:
主核在对内核相关的对象完成初始化工作后,拉起从核。等待从核启动完成,再开始任务调度
初期,从核的启动可以(C部分)可以使用独有的函数。后期,在每个核心初始化时(私有的空闲任务,软件定时器等),应该使用统一的接口
关于任务调度的任务优先级问题,还需要再分析现有的代码
基于原子操作实现自旋锁
// arch
VOID ArchSpinLock(UINT32 *rawlock) // 上锁
INT32 ArchSpinTrylock(UINT32 *rawlock) // 尝试上锁
VOID ArchSpinUnlock(UINT32 *rawlock) // 解锁
// LOS
typedef struct {
UINT32 rawlock;
...
} LosSpinCB;
LOS_SpinLock(LosSpinCB *spin)
LOS_SpinTrylock
LOS_SpinUnlock
LOS_SpinLockSave
LOS_SpinUnlockRestore
...
调整现有的代码,抽象成preCpu,将与任务调度相关的全局变量变成每个核心私有的变量
多核启动框架
多核调度 绑核操作
杨泽霖
2022-4-26
1. 多核调度
2. 亲核特性
3. 多核引导
提供以下类型以及接口
typedef struct {
...
} ArchSpinRawLock;
INT32 ArchCurrCpuid(VOID);// 获取当前核心号 ( 0 , 1 , 2 ... )
VOID ArchSpinInit(ArchSpinRawLock *lock);
VOID ArchSpinLock(ArchSpinRawLock *lock);
VOID ArchSpinUnlock(ArchSpinRawLock *lock);
INT32 ArchSpinTrylock(ArchSpinRawLock *lock);
如果当前不需要开启自旋锁, 这些接口可以由通用的los_core.h内实现几个空函数代替
如果需要开启自旋锁功能, 需要在平台自己的arch目录下添加头文件 los_arch_core.h 并实现上述接口
struct Spinlock {
ArchSpinRawLock rawLock;
...
};
VOID LOS_SpinInit(struct Spinlock *lock);
VOID LOS_SpinLock(struct Spinlock *lock);
INT32 LOS_SpinTrylock(struct Spinlock *lock);
VOID LOS_SpinUnlock(struct Spinlock *lock);
VOID LOS_SpinLockSave(struct Spinlock *lock, UINT32 *intSave);
VOID LOS_SpinUnlockRestore(struct Spinlock *lock, UINT32 intSave);
spinlock死锁检测机制后续可以做到这个模块内
VOID LOS_BitmapSet(UINT32 *bitmap, UINT16 pos);
VOID LOS_BitmapClr(UINT32 *bitmap, UINT16 pos);
UINT16 LOS_HighBitGet(UINT32 bitmap);
UINT16 LOS_LowBitGet(UINT32 bitmap);
typedef struct {
SortLinkAttribute taskSortLinkList; /* 超时等待任务队列, 如果任务是超时等待的状态,
会加入这个队列, 触发超时之后, 唤醒任务进行调度 */
LOS_DL_LIST priQueueList[OS_PRIORITY_QUEUE_NUM]; /* 优先级队列 */
UINT32 queueBitmap; /* 优先级bitmap */
UINT64 responseTime; /* Response time for current CPU tick interrupts */
UINT32 responseID; /* The response ID of the current CPU tick interrupt */
UINT32 idleTaskID; /* 空闲任务id */
UINT32 taskLockCnt; /* 锁调度状态 */
UINT32 schedFlag; /* 当前调度队列的调度状态 */
} SchedRunqueue;
// 全局变量
extern SchedRunqueue g_schedRunqueue[LOSCFG_KERNEL_CORE_NUM];
STATIC INLINE SchedRunqueue *OsGetRunQueue(INT32 cpuid)
{
return &(g_schedRunqueue[cpuid]);
}
先基于现有的调度逻辑, 将于调度有关的全局变量抽象出来, 放在 SchedRunqueue 变量内部
LOS_KernelInit() 主核初始化相关流程
{
...
OsSmpInit(); // 拉起从核
}
// 从核初始化逻辑
VOID OsSmpSecondaryInit(VOID *arg)
{
#if (LOSCFG_BASE_CORE_SWTMR == 1)
OsSwtmrInit();// 初始化软件定时器
#endif
OsIdleTaskCreate();// 初始化从核的空闲任务
OsSchedStart();// 开始调度
}
任务创建之后, 根据它的亲核特性, 将其加入到对应的SchedRunqueue中
如果没有设置亲核属性, 判断当前哪个核心上的任务较少(后面可以换成判断哪个核心负载较低?)
// 根据taskCB判断当前的任务与哪个核相关
INT32 OsPickCpu(LosTaskCB *taskCB)
{
...
return cpuid;
}
每个任务在创建好之后只在固定的核心上进行调度。
注意模块解耦及可裁剪
登录 后才可以发表评论