1. 程式人生 > >LiteOS-任務篇-原始碼分析-任務排程函式

LiteOS-任務篇-原始碼分析-任務排程函式

[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