LiteOS-任務篇-原始碼分析-任務排程函式
阿新 • • 發佈:2020-10-13
[toc]
---
## 前言
* 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** 中斷
![](https://img2020.cnblogs.com/blog/2085252/202010/2085252-20201013093132736-1570889883.png)
* 退出 **osTaskSchedule** 函式,即是返回上層函式
```c
osTaskSchedule
LDR R0, =OS_NVIC_INT_CTRL
LDR R1, =OS_NVIC_PENDSVSET
STR R1, [R0]
BX LR
```
* OS_NVIC_INT_CTRL 定義 ( *位於檔案 ***los_hwi.h*** 中* )
```c
/**
* @ingroup los_hwi
* Interrupt control and status register.
*/
#define OS_NVIC_INT_CTRL 0xE000ED04
```
* OS_NVIC_INT_CTRL 定義 ( *位於檔案 ***los_dispatch_keil.S*** 中* )
```c
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**
```c
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**。
* 語句 `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` 函式中
```c
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** 函式多用於建立任務函式和刪除任務函式。
```c
/*****************************************************************************
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`**;
* 否
* 解鎖中斷
```c
/*****************************************************************************
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` 原始碼參考
* 上述程式碼分析理解時需要了解這個結構體佈局。
```c
/**
* @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*** 中*)
```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原始碼連結](https://gitee.com/LiteOS/LiteOS)
* [常見問題](https://liteos.github.io/porting/faq.html)
* [華為開發者社群](https://developer.huaweicloud.com/)
* [華為LiteOS官方教程](https://support.huaweicloud.com/LiteOS/index.html)
* [我的原始碼](https://gitee.com/lidreaming/LiteOS-mcu)
* 包含 裸機原始碼
* LiteOS 工程模板
* 其它關於 LiteOS 的 demo