LiteOS-任务篇-源码分析-任务调度函数
- 2020 年 10 月 13 日
- 筆記
- /label/LiteOS, /label/lzm, /label/RTOS, /study/LiteOS/note, C语言, LiteOS
目录
前言
- 20201012
- LiteOS 2018
- 建议先瞄一眼 辅助参考代码 章节
笔录草稿
核心源码分析
- 这里主要分析系统调度的汇编部分,也是调度的底层核心部分。
osTaskSchedule函数源码分析
- osTaskSchedule 源码 ( 位于文件 los_dispatch_keil.S 中 )
-
往寄存器 OS_NVIC_INT_CTRL 中写入 OS_NVIC_PENDSVSET 值
- OS_NVIC_INT_CTRL 为 Interrupt Control State Register,该寄存器可配置内容如下
- set a pending Non-Maskable Interrupt (NMI)
- set or clear a pending SVC
- set or clear a pending SysTick
- check for pending exceptions
- check the vector number of the highest priority pended exception
- check the vector number of the active exception.
- 设置如图,触发 PendSV 中断
- OS_NVIC_INT_CTRL 为 Interrupt Control State Register,该寄存器可配置内容如下
-
退出 osTaskSchedule 函数,即是返回上层函数
-
osTaskSchedule
LDR R0, =OS_NVIC_INT_CTRL
LDR R1, =OS_NVIC_PENDSVSET
STR R1, [R0]
BX LR
- OS_NVIC_INT_CTRL 定义 ( 位于文件 los_hwi.h 中 )
/**
* @ingroup los_hwi
* Interrupt control and status register.
*/
#define OS_NVIC_INT_CTRL 0xE000ED04
- OS_NVIC_INT_CTRL 定义 ( 位于文件 los_dispatch_keil.S 中 )
OS_NVIC_PENDSVSET EQU 0x10000000
osPendSV函数源码分析
- PendSV 中断的回调函数就是
void osPendSV(void);
- osPendSV 源码 ( 位于文件 los_dispatch_keil.S 中 )
- 读取 PRIMASK 的值到 R12 中,即是保存中断状态
- 屏蔽全局中断
- 判断是否调用 TaskSwitch 函数
- 如果宏 LOSCFG_BASE_CORE_TSK_MONITOR 为 NO,则运行 TaskSwitch 函数
- 如果宏 LOSCFG_BASE_CORE_TSK_MONITOR 为 YES,则在下面运行 osTaskSwitchCheck 函数
- 压栈保护寄存器 R12和LR
- 运行 R2 函数,也就是 osTaskSwitchCheck 函数
- 源码解析路径:
LOS_KernelInit() --> osTaskMonInit() --> g_pfnTskSwitchHook = osTaskSwitchCheck;
- 读者可以自己追踪一下
- 源码解析路径:
- 恢复 R12和LR
osPendSV
MRS R12, PRIMASK
CPSID I
LDR R2, =g_pfnTskSwitchHook ; C: R2 = &g_pfnTskSwitchHook;
LDR R2, [R2] ; C: R2 = *R2; ==》 R2 = g_pfnTskSwitchHook;
CBZ R2, TaskSwitch ; C: if(g_pfnTskSwitchHook == 0) TaskSwitch();
PUSH {R12, LR} ; 将 R12 和 LR 寄存器压栈
BLX R2 ; 跳到 R2
POP {R12, LR} ; 出栈到寄存器 R12 和 LR
- PRIMASK 说明
- 这是个只有单一比特的寄存器
- 被置 1 后,就关掉所有可屏蔽的异常,只剩下 NMI 和 硬fault 可以响应
- 缺省值是0,表示没有关中断。
- 指令 CBZ
- 比较 为 0 则跳转,如:
CBZ x1,fun
; 表示如果 x1 为 0,则跳转到 fun。
- 比较 为 0 则跳转,如:
- 语句
PUSH {R12, LR}
和POP {R12, LR}
(个人理解,望指正)- {} 内先排序,根据寄存器 PS 的走向排序,最终目标是,下面那点
- 小端模式:低编号寄存器对应低地址
PUSH {R12, LR}
- 顺序:LR R12
- 压栈:先压 LR,PS -= 4
- 压栈:再压 R12,PS -= 4
PUSH {R12, LR}
- 顺序:R12 LR
- 压栈:先出 R12,PS += 4
- 压栈:再压 LR,PS += 4
TaskSwitch函数源码分析
- 如果用户没有开启任务堆栈监测,即宏 LOSCFG_BASE_CORE_TSK_MONITOR 配置为 NO,就运行本函数。
- PSP 更新给 R0
- 手动把 R4-R12 压栈
- R0-R3,R12,LR,PC,xPSR 这些寄存器已经自动压栈了
- 更新当前运行任务栈 g_stLosTask.pstRunTask.pStackPointer 指针
- 更新当前任务状态,取消 OS_TASK_STATUS_RUNNING 运行态
- 先获取当前任务的状态寄存器
- 再取消 OS_TASK_STATUS_RUNNING 运行态
- 最后再赋值回
g_stLosTask.pstRunTask.usTaskStatus
- 更新当前运行任务变量
*g_stLosTask.pstRunTask = *g_stLosTask.pstNewTask;
- 更新准备运行的任务的状态,更新运行态 OS_TASK_STATUS_RUNNING (注:此时
*g_stLosTask.pstRunTask
和*g_stLosTask.pstNewTask
是一样的,指向同一个任务) - 准备运行的任务手动出栈
- 更新准备运行的任务的 PSP 值
- 恢复原有的中断状态
- 返回到上层函数中,如
osSchedule
或LOS_Schedule
函数中
TaskSwitch
MRS R0, PSP ;// R0 = PSP;
STMFD R0!, {R4-R12} ;// 手动压栈,先减再压,小端,且栈往下生长
LDR R5, =g_stLosTask ;// R5 = g_stLosTask; ==> R5 = g_stLosTask.pstRunTask;
LDR R6, [R5] ;// R6 = *(g_stLosTask.pstRunTask); ==> R6 = g_stLosTask.pstRunTask.pStackPointer;
STR R0, [R6] ;// *(g_stLosTask.pstRunTask.pStackPointer) = R0;
LDRH R7, [R6 , #4] ;// R7 = *(&(g_stLosTask.pstRunTask.usTaskStatus)); ==> R7 = g_stLosTask.pstRunTask.usTaskStatus;
MOV R8,#OS_TASK_STATUS_RUNNING ;// R8 = OS_TASK_STATUS_RUNNING;
BIC R7, R7, R8 ;// R7 &= ~R8;
STRH R7, [R6 , #4] ;// g_stLosTask.pstRunTask.usTaskStatus = R7;
LDR R0, =g_stLosTask ;// R0 = g_stLosTask; ==> R0 = g_stLosTask.pstRunTask;
LDR R0, [R0, #4] ;// R0 = *(g_stLosTask.pstNewTask); ==> R0 = g_stLosTask.pstNewTask.pStackPointer;
STR R0, [R5] ;// g_stLosTask.pstRunTask.pStackPointer = g_stLosTask.pstNewTask.pStackPointer; ==> *g_stLosTask.pstRunTask = *g_stLosTask.pstNewTask;
LDRH R7, [R0 , #4] ;// R7 = *(&(g_stLosTask.pstNewTask.usTaskStatus)); ==> R7 = g_stLosTask.pstNewTask.usTaskStatus;
MOV R8, #OS_TASK_STATUS_RUNNING ;// R8 = OS_TASK_STATUS_RUNNING;
ORR R7, R7, R8 ;// R7 |= R8;
STRH R7, [R0 , #4] ;// g_stLosTask.pstNewTask.usTaskStatus = R7;
LDR R1, [R0] ;// R1 = *(g_stLosTask.pstNewTask.pStackPointer);
LDMFD R1!, {R4-R12} ;// 手动出栈,先出栈后增,小端,且栈往上生长
MSR PSP, R1 ;// PSP = R1; // 更新 PSP 值
MSR PRIMASK, R12 ;// 恢复原有的中断状态
BX LR ;// 返回到上层函数中,如 `osSchedule` 或 `LOS_Schedule` 函数中
ALIGN
END
调度上层源码分析
osSchedule函数源码分析
- osSchedule 函数多用于创建任务函数和删除任务函数。
/*****************************************************************************
Function : osSchedule
Description : task scheduling
Input : None
Output : None
Return : None
*****************************************************************************/
LITE_OS_SEC_TEXT VOID osSchedule(VOID)
{
osTaskSchedule();
}
LOS_Schedule函数源码分析
- LOS_Schedule 函数为系统常用的调度函数。
- 简单流程
- 锁中断
- 从就绪列表中获取最合适的任务,赋值给 g_stLosTask.pstNewTask ,为下一个运行的任务
- 判断当前运行的任务和就绪列表中最适合的任务是否为同一个任务
- 是
- 判断是否锁任务调度
- 是
- 否
- 解锁中断
- 进行调度操作:
osTaskSchedule();
return
;
- 判断是否锁任务调度
- 否
- 是
- 解锁中断
/*****************************************************************************
Function : LOS_Schedule
Description : Function to determine whether task scheduling is required
Input : None
Output : None
Return : None
*****************************************************************************/
LITE_OS_SEC_TEXT VOID LOS_Schedule(VOID)
{
UINTPTR uvIntSave;
uvIntSave = LOS_IntLock(); // 锁中断
/* Find the highest task */
g_stLosTask.pstNewTask = LOS_DL_LIST_ENTRY(osPriqueueTop(), LOS_TASK_CB, stPendList); // 从就绪列表中获取最合适的任务,赋值给 g_stLosTask.pstNewTask ,为下一个运行的任务
/* In case that running is not highest then reschedule */
if (g_stLosTask.pstRunTask != g_stLosTask.pstNewTask) // 不是同一个任务就进行调度准备
{
if ((!g_usLosTaskLock)) // 判断是否锁任务了
{
(VOID)LOS_IntRestore(uvIntSave); // 解锁中断
osTaskSchedule(); // 调度操作
return; // 返回
}
}
(VOID)LOS_IntRestore(uvIntSave); // 解锁中断
}
辅助参考代码
任务控制块 LOS_TASK_CB
源码参考
- 上述代码分析理解时需要了解这个结构体布局。
/**
* @ingroup los_task
* Define the task control block structure.
*/
typedef struct tagTaskCB
{
VOID *pStackPointer; /**< Task stack pointer */
UINT16 usTaskStatus;
UINT16 usPriority;
UINT32 uwStackSize; /**< Task stack size */
UINT32 uwTopOfStack; /**< Task stack top */
UINT32 uwTaskID; /**< Task ID */
TSK_ENTRY_FUNC pfnTaskEntry; /**< Task entrance function */
VOID *pTaskSem; /**< Task-held semaphore */
VOID *pTaskMux; /**< Task-held mutex */
UINT32 uwArg; /**< Parameter */
CHAR *pcTaskName; /**< Task name */
LOS_DL_LIST stPendList;
LOS_DL_LIST stTimerList;
UINT32 uwIdxRollNum;
EVENT_CB_S uwEvent;
UINT32 uwEventMask; /**< Event mask */
UINT32 uwEventMode; /**< Event mode */
VOID *puwMsg; /**< Memory allocated to queues */
} LOS_TASK_CB;
LiteOS中断向量表(二次命名版)
- 中断向量表源码 (位于文件 los_hwi.c 中)
HWI_PROC_FUNC m_pstHwiForm[OS_VECTOR_CNT] =
{
(HWI_PROC_FUNC)0, // [0] Top of Stack
(HWI_PROC_FUNC)Reset_Handler, // [1] reset
(HWI_PROC_FUNC)osHwiDefaultHandler, // [2] NMI Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [3] Hard Fault Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [4] MPU Fault Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [5] Bus Fault Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [6] Usage Fault Handler
(HWI_PROC_FUNC)0, // [7] Reserved
(HWI_PROC_FUNC)0, // [8] Reserved
(HWI_PROC_FUNC)0, // [9] Reserved
(HWI_PROC_FUNC)0, // [10] Reserved
(HWI_PROC_FUNC)osHwiDefaultHandler, // [11] SVCall Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [12] Debug Monitor Handler
(HWI_PROC_FUNC)0, // [13] Reserved
(HWI_PROC_FUNC)osPendSV, // [14] PendSV Handler
(HWI_PROC_FUNC)osHwiDefaultHandler, // [15] SysTick Handler
};
参考
链接
- LiteOS源码链接
- 常见问题
- 华为开发者社区
- 华为LiteOS官方教程
- 我的源码
- 包含 裸机源码
- LiteOS 工程模板
- 其它关于 LiteOS 的 demo 及 note