鴻蒙輕內核M核源碼分析系列六任務及任務調度(2)任務模塊
任務是操作系統一個重要的概念,是競爭系統資源的最小運行單元。任務可以使用或等待CPU、使用內存空間等系統資源,并獨立于其它任務運行。鴻蒙輕內核的任務模塊可以給用戶提供多個任務,實現任務間的切換,幫助用戶管理業務程序流程。
接下來,我們看下任務模塊的結構體,任務初始化,任務常用操作的源代碼。
1、任務模塊的結構體定義
在文件kernel\include\los_task.h定義的任務控制塊結構體LosTaskCB,源代碼如下,結構體成員的解釋見注釋部分。
- typedef struct {
- VOID *stackPointer; /* 任務棧指針 */
- UINT16 taskStatus; /* 任務狀態 */
- UINT16 priority; /* 任務優先級 */
- INT32 timeSlice; /* 剩余的時間片 */
- UINT32 waitTimes;
- SortLinkList sortList; /* 任務超時排序鏈表節點 */
- UINT64 startTime;
- UINT32 stackSize; /* 任務棧大小 */
- UINT32 topOfStack; /* 棧頂指針 */
- UINT32 taskID; /* 任務編號Id */
- TSK_ENTRY_FUNC taskEntry; /* 任務入口函數 */
- VOID *taskSem; /* 任務持有的信號量 */
- VOID *taskMux; /* 導致任務阻塞的互斥鎖 */
- UINT32 arg; /* 任務入口函數的參數 */
- CHAR *taskName; /* 任務名稱 */
- LOS_DL_LIST pendList; /* 就緒隊列等鏈表節點 */
- LOS_DL_LIST timerList; /* 任務超時排序鏈表節點 */
- EVENT_CB_S event;
- UINT32 eventMask; /* 事件掩碼 */
- UINT32 eventMode; /* 事件模式 */
- VOID *msg; /* 分給給隊列的內存*/
- INT32 errorNo;
- } LosTaskCB;
另外一個比較重要的結構體是TSK_INIT_PARAM_S,創建任務時,需要指定任務初始化的參數。源代碼如下,結構體成員的解釋見注釋部分。
- typedef struct tagTskInitParam {
- TSK_ENTRY_FUNC pfnTaskEntry; /** 任務入口函數 */
- UINT16 usTaskPrio; /** 任務參數 */
- UINT32 uwStackSize; /** 任務棧大小 */
- CHAR *pcName; /** 任務名稱 */
- UINT32 uwResved; /** 保留 */
- } TSK_INIT_PARAM_S;
2、任務模塊初始化
在系統啟動時,在kernel\src\los_init.c中調用OsTaskInit()進行任務模塊初始化,還會調用OsIdleTaskCreate()創建空閑任務。
2.1 任務模塊初始化
函數OsTaskInit()定義在kernel\src\los_task.c,我們分析下這個函數的執行過程。
⑴處代碼根據開發板配置的最大任務數g_taskMaxNum,計算需要申請的內存大小size,為任務控制塊TCB數組(也叫作任務池)g_taskCBArray申請內存。為什么比最大任務數多申請一個呢?在刪除任務時會使用。下文分析刪除任務的源碼時再詳細講解其用意。
⑵處代碼初始化雙向鏈表g_losFreeTask用作空閑的任務鏈表、g_taskRecyleList可以回收的任務鏈表。
⑶處循環初始化每一個任務,任務狀態未使用OS_TASK_STATUS_UNUSED,初始化任務Id,并把任務掛在空閑任務鏈表上。
⑷處初始化全局變量LosTask g_losTask,該全局變量維護當前運行的任務和要調度執行的任務。初始化任務池時,設置當前運行的任務為g_taskCBArray[g_taskMaxNum]。⑸處空閑任務編號暫時設置為無效值,后續創建空閑任務時再設置空閑任務編號。
優先級隊列,詳細的代碼實現剖析,參見之前的源碼剖析文章。⑸處互斥鎖死鎖檢測的調測特性的,后續系列文章專題進行講解。⑹處代碼初始化排序鏈表,詳細的代碼實現剖析,參見之前的源碼剖析文章。⑺處如果開啟了惰性棧,計算TCB的成員變量stackFrame在其結構體中的偏移量g_stackFrameOffLenInTcb。
- LITE_OS_SEC_TEXT_INIT UINT32 OsTaskInit(VOID)
- {
- UINT32 size;
- UINT32 index;
- ⑴ size = (g_taskMaxNum + 1) * sizeof(LosTaskCB);
- g_taskCBArray = (LosTaskCB *)LOS_MemAlloc(m_aucSysMem0, size);
- if (g_taskCBArray == NULL) {
- return LOS_ERRNO_TSK_NO_MEMORY;
- }
- (VOID)memset_s(g_taskCBArray, size, 0, size);
- ⑵ LOS_ListInit(&g_losFreeTask);
- LOS_ListInit(&g_taskRecyleList);
- ⑶ for (index = 0; index <= LOSCFG_BASE_CORE_TSK_LIMIT; index++) {
- g_taskCBArray[index].taskStatus = OS_TASK_STATUS_UNUSED;
- g_taskCBArray[index].taskID = index;
- LOS_ListTailInsert(&g_losFreeTask, &g_taskCBArray[index].pendList);
- }
- // Ignore the return code when matching CSEC rule 6.6(4).
- ⑷ (VOID)memset_s((VOID *)(&g_losTask), sizeof(g_losTask), 0, sizeof(g_losTask));
- g_losTask.runTask = &g_taskCBArray[g_taskMaxNum];
- g_losTask.runTask->taskID = index;
- g_losTask.runTask->taskStatus = (OS_TASK_STATUS_UNUSED | OS_TASK_STATUS_RUNNING);
- g_losTask.runTask->priority = OS_TASK_PRIORITY_LOWEST + 1;
- ⑸ g_idleTaskID = OS_INVALID;
- ⑹ return OsSchedInit();
- }
2.2 創建空閑任務IdleCore000
除了初始化任務池,在系統啟動階段還會創建idle空閑任務。⑴處設置任務初始化參數時,空閑任務的入口執行函數為OsIdleTask()。⑵處調用函數把空閑任務狀態設置為就緒狀態。
- LITE_OS_SEC_TEXT_INIT UINT32 OsIdleTaskCreate(VOID)
- {
- UINT32 retVal;
- TSK_INIT_PARAM_S taskInitParam;
- // Ignore the return code when matching CSEC rule 6.6(4).
- (VOID)memset_s((VOID *)(&taskInitParam), sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
- ⑴ taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)OsIdleTask;
- taskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE;
- taskInitParam.pcName = "IdleCore000";
- taskInitParam.usTaskPrio = OS_TASK_PRIORITY_LOWEST;
- retVal = LOS_TaskCreateOnly(&g_idleTaskID, &taskInitParam);
- if (retVal != LOS_OK) {
- return retVal;
- }
- ⑵ OsSchedSetIdleTaskSchedPartam(OS_TCB_FROM_TID(g_idleTaskID));
- return LOS_OK;
- }
我們看下空閑任務的入口執行函數為OsIdleTask(),它調用OsRecyleFinishedTask()回收任務棧資源,后文會分析如何回收任務資源。
- LITE_OS_SEC_TEXT WEAK VOID OsIdleTask(VOID)
- {
- while (1) {
- OsRecyleFinishedTask();
- HalEnterSleep(OS_SYS_DEEP_SLEEP);
- }
- }
3、任務模塊常用操作
3.1 創建和刪除任務
3.1.1 創建任務
鴻蒙輕內核提供了2個創建任務的函數,有LOS_TaskCreate、LOS_TaskCreateOnly。LOS_TaskCreate和LOS_TaskCreateOnly的區別是,前者創建任務完畢就使任務進入就緒狀態,并觸發調度,如果就緒隊列中沒有更高優先級的任務,則運行該任務。后者只創建任務,設置任務狀態為阻塞suspend狀態,需要開發者去調用LOS_TaskResume使該任務進入ready狀態。
函數LOS_TaskCreate代碼如下,可以看出創建任務的時候,調用⑴處的函數LOS_TaskCreateOnly()來創建任務。創建任務后,執行⑵處的代碼使任務進入ready就緒隊列,如果系統啟動完成,允許任務調度,則執行⑶觸發任務調度。如果新創建的任務優先級最高,則會被調度運行。
- LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskID, TSK_INIT_PARAM_S *taskInitParam)
- {
- UINT32 retVal;
- UINTPTR intSave;
- LosTaskCB *taskCB = NULL;
- ⑴ retVal = LOS_TaskCreateOnly(taskID, taskInitParam);
- if (retVal != LOS_OK) {
- return retVal;
- }
- taskCB = OS_TCB_FROM_TID(*taskID);
- intSave = LOS_IntLock();
- #if (LOSCFG_BASE_CORE_CPUP == 1)
- g_cpup[taskCB->taskID].cpupID = taskCB->taskID;
- g_cpup[taskCB->taskID].status = taskCB->taskStatus;
- #endif
- ⑵ OsSchedTaskEnQueue(taskCB);
- LOS_IntRestore(intSave);
- ⑶ if (g_taskScheduled) {
- LOS_Schedule();
- }
- return LOS_OK;
- }
我們接著分析下如何使用函數UINT32 LOS_TaskCreateOnly()創建任務。⑴處調用OsTaskInitParamCheck()檢測創建任務的參數的合法性。⑵處調用函數回收釋放的任務。⑶處如果任務池為空,無法創建任務,返回錯誤碼。⑷處從任務池獲取一個空閑的任務控制塊taskCB,然后從空閑任務鏈表中刪除。⑸處根據指定的任務棧大小為任務棧申請內存,⑹處判斷任務棧內存申請釋放成功,如果申請失敗,則把任務控制塊歸還到空閑任務鏈表中,并返回錯誤碼。⑺處調用函數初始化任務棧,更新任務控制塊成員信息。詳細見后面對該函數的分析。
- LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreateOnly(UINT32 *taskID, TSK_INIT_PARAM_S *taskInitParam)
- {
- UINTPTR intSave;
- VOID *topOfStack = NULL;
- LosTaskCB *taskCB = NULL;
- UINT32 retVal;
- if (taskID == NULL) {
- return LOS_ERRNO_TSK_ID_INVALID;
- }
- ⑴ retVal = OsTaskInitParamCheck(taskInitParam);
- if (retVal != LOS_OK) {
- return retVal;
- }
- ⑵ OsRecyleFinishedTask();
- intSave = LOS_IntLock();
- ⑶ if (LOS_ListEmpty(&g_losFreeTask)) {
- retVal = LOS_ERRNO_TSK_TCB_UNAVAILABLE;
- OS_GOTO_ERREND();
- }
- ⑷ taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_losFreeTask));
- LOS_ListDelete(LOS_DL_LIST_FIRST(&g_losFreeTask));
- LOS_IntRestore(intSave);
- #if (LOSCFG_EXC_HRADWARE_STACK_PROTECTION == 1)
- UINTPTR stackPtr = (UINTPTR)LOS_MemAllocAlign(OS_TASK_STACK_ADDR, taskInitParam->uwStackSize +
- OS_TASK_STACK_PROTECT_SIZE, OS_TASK_STACK_PROTECT_SIZE);
- topOfStack = (VOID *)(stackPtr + OS_TASK_STACK_PROTECT_SIZE);
- #else
- ⑸ topOfStack = (VOID *)LOS_MemAllocAlign(OS_TASK_STACK_ADDR, taskInitParam->uwStackSize,
- LOSCFG_STACK_POINT_ALIGN_SIZE);
- #endif
- ⑹ if (topOfStack == NULL) {
- intSave = LOS_IntLock();
- LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
- LOS_IntRestore(intSave);
- return LOS_ERRNO_TSK_NO_MEMORY;
- }
- ⑺ retVal = OsNewTaskInit(taskCB, taskInitParam, topOfStack);
- if (retVal != LOS_OK) {
- return retVal;
- }
- *taskID = taskCB->taskID;
- OsHookCall(LOS_HOOK_TYPE_TASK_CREATE, taskCB);
- return retVal;
- LOS_ERREND:
- LOS_IntRestore(intSave);
- return retVal;
- }
我們看下創建任務函數調用的函數OsRecyleFinishedTask(),該函數在系統進入空閑時也會調用。刪除運行狀態的任務時,會把任務掛在雙向鏈表里g_taskRecyleList。任務回收函數就用來回收此類任務,實現任務資源回收。我們分析下它的代碼。⑴處循環遍歷回收鏈表,⑵從回收鏈表獲取第一個任務taskCB,從回收鏈表刪除并插入到空閑任務鏈表里。任務棧保護在后續系列再深入分析,繼續往下看代碼,⑶處獲取任務棧棧頂指針,接著調用內存釋放函數來釋放任務棧占用的內存,并設置任務棧的棧頂為空。
- STATIC VOID OsRecyleFinishedTask(VOID)
- {
- LosTaskCB *taskCB = NULL;
- UINTPTR intSave;
- UINTPTR stackPtr;
- intSave = LOS_IntLock();
- ⑴ while (!LOS_ListEmpty(&g_taskRecyleList)) {
- ⑵ taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_taskRecyleList));
- LOS_ListDelete(LOS_DL_LIST_FIRST(&g_taskRecyleList));
- LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
- #if (LOSCFG_EXC_HRADWARE_STACK_PROTECTION == 1)
- stackPtr = taskCB->topOfStack - OS_TASK_STACK_PROTECT_SIZE;
- #else
- ⑶ stackPtr = taskCB->topOfStack;
- #endif
- (VOID)LOS_MemFree(OS_TASK_STACK_ADDR, (VOID *)stackPtr);
- taskCB->topOfStack = (UINT32)NULL;
- }
- LOS_IntRestore(intSave);
- }
我們繼續分析下函數OsNewTaskInit(),⑴處調用函數初始化任務棧,上一系列已經分析過該函數,代碼的其余部分用來更新任務控制塊的成員信息,比如⑵處任務狀態設置為阻塞狀態。
- LITE_OS_SEC_TEXT_INIT UINT32 OsNewTaskInit(LosTaskCB *taskCB, TSK_INIT_PARAM_S *taskInitParam, VOID *topOfStack)
- {
- ⑴ taskCB->stackPointer = HalTskStackInit(taskCB->taskID, taskInitParam->uwStackSize, topOfStack);
- taskCB->arg = taskInitParam->uwArg;
- taskCB->topOfStack = (UINT32)(UINTPTR)topOfStack;
- taskCB->stackSize = taskInitParam->uwStackSize;
- taskCB->taskSem = NULL;
- taskCB->taskMux = NULL;
- ⑵ taskCB->taskStatus = OS_TASK_STATUS_SUSPEND;
- taskCB->priority = taskInitParam->usTaskPrio;
- taskCB->timeSlice = 0;
- taskCB->waitTimes = 0;
- taskCB->taskEntry = taskInitParam->pfnTaskEntry;
- taskCB->event.uwEventID = OS_NULL_INT;
- taskCB->eventMask = 0;
- taskCB->taskName = taskInitParam->pcName;
- taskCB->msg = NULL;
- SET_SORTLIST_VALUE(&taskCB->sortList, OS_SORT_LINK_INVALID_TIME);
- return LOS_OK;
- }
3.1.2 刪除任務UINT32 LOS_TaskDelete()
該函數根據傳入的參數UINT32 taskId刪除任務。我們分析下刪除任務的源代碼,⑴處檢驗傳入的參數,⑵處如果任務還未創建,返回錯誤碼。⑶處如果刪除的任務正在運行,又處于鎖任務調度情況下,打印信息,告訴用戶不推薦在鎖任務調度期間進行任務刪除,然后執行⑷,把全局變量賦值0來解鎖任務調度。
⑸處調用函數處理任務狀態,如果處于就緒狀態設置為非就緒狀態,并從就緒隊列刪除。如果處于阻塞狀態,從阻塞隊列中刪除。如果任務處于超時等待狀態,從超時排序鏈表中刪除。⑹恢復任務控制塊事件相關的成員信息。⑺如果任務正在運行,設置任務為未使用狀態,接著調用函數OsRunningTaskDelete()把任務放入回收鏈表,然后主動觸發任務調度,稍后詳細分析該函數。如果刪除的任務不是出于運行狀態,則執行⑻,設置任務為未使用狀態,接著把任務回收到空閑任務鏈表里,然后獲取任務棧的棧頂指針,調用內存釋放函數釋放任務棧的內存。
- LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskDelete(UINT32 taskID)
- {
- UINTPTR intSave;
- LosTaskCB *taskCB = OS_TCB_FROM_TID(taskID);
- UINTPTR stackPtr;
- ⑴ UINT32 ret = OsCheckTaskIDValid(taskID);
- if (ret != LOS_OK) {
- return ret;
- }
- intSave = LOS_IntLock();
- ⑵ if ((taskCB->taskStatus) & OS_TASK_STATUS_UNUSED) {
- LOS_IntRestore(intSave);
- return LOS_ERRNO_TSK_NOT_CREATED;
- }
- /* If the task is running and scheduler is locked then you can not delete it */
- ⑶ if (((taskCB->taskStatus) & OS_TASK_STATUS_RUNNING) && (g_losTaskLock != 0)) {
- PRINT_INFO("In case of task lock, task deletion is not recommended\n");
- ⑷ g_losTaskLock = 0;
- }
- OsHookCall(LOS_HOOK_TYPE_TASK_DELETE, taskCB);
- ⑸ OsSchedTaskExit(taskCB);
- ⑹ taskCB->event.uwEventID = OS_NULL_INT;
- taskCB->eventMask = 0;
- #if (LOSCFG_BASE_CORE_CPUP == 1)
- // Ignore the return code when matching CSEC rule 6.6(4).
- (VOID)memset_s((VOID *)&g_cpup[taskCB->taskID], sizeof(OsCpupCB), 0, sizeof(OsCpupCB));
- #endif
- if (taskCB->taskStatus & OS_TASK_STATUS_RUNNING) {
- ⑺ taskCB->taskStatus = OS_TASK_STATUS_UNUSED;
- OsRunningTaskDelete(taskID, taskCB);
- LOS_IntRestore(intSave);
- LOS_Schedule();
- return LOS_OK;
- } else {
- ⑻ taskCB->taskStatus = OS_TASK_STATUS_UNUSED;
- LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
- #if (LOSCFG_EXC_HRADWARE_STACK_PROTECTION == 1)
- stackPtr = taskCB->topOfStack - OS_TASK_STACK_PROTECT_SIZE;
- #else
- stackPtr = taskCB->topOfStack;
- #endif
- (VOID)LOS_MemFree(OS_TASK_STACK_ADDR, (VOID *)stackPtr);
- taskCB->topOfStack = (UINT32)NULL;
- }
- LOS_IntRestore(intSave);
- return LOS_OK;
- }
我們看下函數OsRunningTaskDelete()的源碼。⑴處把當前運行的任務放入待回收鏈表里,然后執行⑵把當前運行的任務放入任務池的最后一個位置g_taskCBArray[g_taskMaxNum]。為什么這么操作呢?等后續分析源碼的時候再來解答。
- LITE_OS_SEC_TEXT_INIT STATIC_INLINE VOID OsRunningTaskDelete(UINT32 taskID, LosTaskCB *taskCB)
- {
- ⑴ LOS_ListTailInsert(&g_taskRecyleList, &taskCB->pendList);
- ⑵ g_losTask.runTask = &g_taskCBArray[g_taskMaxNum];
- g_losTask.runTask->taskID = taskID;
- g_losTask.runTask->taskStatus = taskCB->taskStatus | OS_TASK_STATUS_RUNNING;
- g_losTask.runTask->topOfStack = taskCB->topOfStack;
- g_losTask.runTask->taskName = taskCB->taskName;
- }
3.2 控制任務狀態
3.2.1 恢復掛起的任務LOS_TaskResume()
恢復掛起的任務,使該任務進入就緒狀態,和下文中的LOS_TaskSuspend()成對使用。⑴處獲取任務的TCB,⑵處對任務狀態進行判斷,如果任務未創建或者非阻塞狀態,則返回錯誤碼。執行⑶設置任務狀態為非掛起狀態。⑶處獲取任務的狀態進行判斷,如果任務沒有創建或者不是掛起狀態,則返回相應的錯誤碼。 ⑷檢查任務狀態是否為OS_CHECK_TASK_BLOCK,即(OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND | OS_TASK_STATUS_SUSPEND)中的一種,這幾個狀態影響恢復掛起的任務。如果非上述幾個狀態,執行⑸調用函數,把任務狀態改為就緒狀態,插入任務就緒隊列。如果支持支持調度,則執行⑹觸發調度。
- LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskResume(UINT32 taskID)
- {
- UINTPTR intSave;
- LosTaskCB *taskCB = NULL;
- UINT16 tempStatus;
- UINT32 retErr = OS_ERROR;
- if (taskID > LOSCFG_BASE_CORE_TSK_LIMIT) {
- return LOS_ERRNO_TSK_ID_INVALID;
- }
- ⑴ taskCB = OS_TCB_FROM_TID(taskID);
- intSave = LOS_IntLock();
- tempStatus = taskCB->taskStatus;
- ⑵ if (tempStatus & OS_TASK_STATUS_UNUSED) {
- retErr = LOS_ERRNO_TSK_NOT_CREATED;
- OS_GOTO_ERREND();
- } else if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
- retErr = LOS_ERRNO_TSK_NOT_SUSPENDED;
- OS_GOTO_ERREND();
- }
- ⑶ taskCB->taskStatus &= (~OS_TASK_STATUS_SUSPEND);
- ⑷ if (!(taskCB->taskStatus & OS_CHECK_TASK_BLOCK)) {
- ⑸ OsSchedTaskEnQueue(taskCB);
- if (g_taskScheduled) {
- LOS_IntRestore(intSave);
- ⑹ LOS_Schedule();
- return LOS_OK;
- }
- }
- LOS_IntRestore(intSave);
- return LOS_OK;
- LOS_ERREND:
- LOS_IntRestore(intSave);
- return retErr;
- }
3.2.2 掛起指定的任務LOS_TaskSuspend()
函數用于掛起指定的任務。⑴處獲取任務的TCB,⑵處開始獲取任務的狀態進行判斷,如果任務沒有創建、任務已經掛起,返回相應的錯誤碼。⑶處如果任務是運行狀態,并且鎖任務調度時,跳轉到LOS_ERREND結束掛起操作。⑷處如果任務是就緒狀態,調用函數從就緒隊列出隊,并取消任務的就緒狀態。⑸處語句設置任務狀態為阻塞狀態。⑹如果掛起的是當前運行的任務,則會主動觸發調度。
- LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskSuspend(UINT32 taskID)
- {
- UINTPTR intSave;
- LosTaskCB *taskCB = NULL;
- UINT16 tempStatus;
- UINT32 retErr;
- retErr = OsCheckTaskIDValid(taskID);
- if (retErr != LOS_OK) {
- return retErr;
- }
- ⑴ taskCB = OS_TCB_FROM_TID(taskID);
- intSave = LOS_IntLock();
- ⑵ tempStatus = taskCB->taskStatus;
- if (tempStatus & OS_TASK_STATUS_UNUSED) {
- retErr = LOS_ERRNO_TSK_NOT_CREATED;
- OS_GOTO_ERREND();
- }
- if (tempStatus & OS_TASK_STATUS_SUSPEND) {
- retErr = LOS_ERRNO_TSK_ALREADY_SUSPENDED;
- OS_GOTO_ERREND();
- }
- ⑶ if ((tempStatus & OS_TASK_STATUS_RUNNING) && (g_losTaskLock != 0)) {
- retErr = LOS_ERRNO_TSK_SUSPEND_LOCKED;
- OS_GOTO_ERREND();
- }
- ⑷ if (tempStatus & OS_TASK_STATUS_READY) {
- OsSchedTaskDeQueue(taskCB);
- }
- ⑸ taskCB->taskStatus |= OS_TASK_STATUS_SUSPEND;
- OsHookCall(LOS_HOOK_TYPE_MOVEDTASKTOSUSPENDEDLIST, taskCB);
- ⑹ if (taskID == g_losTask.runTask->taskID) {
- LOS_IntRestore(intSave);
- LOS_Schedule();
- return LOS_OK;
- }
- LOS_IntRestore(intSave);
- return LOS_OK;
- LOS_ERREND:
- LOS_IntRestore(intSave);
- return retErr;
- }
3.2.3 任務延時等待LOS_TaskDelay()
任務延時等待,釋放CPU,等待時間到期后該任務會重新進入就緒狀態。
⑴處代碼判斷系統處于中斷,如果是,則返回錯誤碼,不允許任務延時等待。
⑵如果處于鎖任務調度期間,則返回錯誤碼。
⑶處如果延遲的時間為0,則執行讓權操作,否則執行。
⑷調用函數OsSchedDelay()把當前任務設置為延時等待狀態,然后調用LOS_Schedule()觸發調度。
- LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 tick)
- {
- UINTPTR intSave;
- ⑴ if (OS_INT_ACTIVE) {
- return LOS_ERRNO_TSK_DELAY_IN_INT;
- }
- ⑵ if (g_losTaskLock != 0) {
- return LOS_ERRNO_TSK_DELAY_IN_LOCK;
- }
- OsHookCall(LOS_HOOK_TYPE_TASK_DELAY, tick);
- ⑶ if (tick == 0) {
- return LOS_TaskYield();
- } else {
- intSave = LOS_IntLock();
- ⑷ OsSchedDelay(g_losTask.runTask, tick);
- OsHookCall(LOS_HOOK_TYPE_MOVEDTASKTODELAYEDLIST, g_losTask.runTask);
- LOS_IntRestore(intSave);
- LOS_Schedule();
- }
- return LOS_OK;
- }
另外還提供了函數LOS_Msleep()和LOS_UDelay(),前者以毫秒為單位進行延遲等待。后者也是以毫秒為單位進行延遲等待,但是不會觸發任務調度,當前任務不會釋放CPU。
- LITE_OS_SEC_TEXT_MINOR VOID LOS_Msleep(UINT32 mSecs)
- {
- UINT32 interval;
- if (OS_INT_ACTIVE) {
- return;
- }
- if (mSecs == 0) {
- interval = 0;
- } else {
- interval = LOS_MS2Tick(mSecs);
- if (interval == 0) {
- interval = 1;
- }
- }
- (VOID)LOS_TaskDelay(interval);
- }
- VOID LOS_UDelay(UINT64 microseconds)
- {
- UINT64 endTime;
- if (microseconds == 0) {
- return;
- }
- endTime = (microseconds / OS_SYS_US_PER_SECOND) * OS_SYS_CLOCK +
- (microseconds % OS_SYS_US_PER_SECOND) * OS_SYS_CLOCK / OS_SYS_US_PER_SECOND;
- endTime = LOS_SysCycleGet() + endTime;
- while (LOS_SysCycleGet() < endTime) {
- }
- return;
- }
3.2.4 任務讓權LOS_TaskYield()
讓權函數通過把當前任務時間片設置為0,釋放CPU占用,重新調度給其他高優先級任務執行。⑴處調用函數把當前任務時間片設置為0,然后執行⑵主動觸發任務調度。
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskYield(VOID)
- {
- UINTPTR intSave;
- intSave = LOS_IntLock();
- ⑴ OsSchedYield();
- LOS_IntRestore(intSave);
- ⑵ LOS_Schedule();
- return LOS_OK;
- }
接下來看下函數OsSchedYield()的源碼。代碼很簡單,獲取當前運行的任務,然后把其時間片設置為0,如下:
- VOID OsSchedYield(VOID)
- {
- LosTaskCB *runTask = g_losTask.runTask;
- runTask->timeSlice = 0;
- }
3.3 控制任務調度
3.3.1 鎖任務調度LOS_TaskLock()
鎖任務調度LOS_TaskLock()比較簡單,把任務鎖調度計數器全局變量增加1即可,代碼如下。
- LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskLock(VOID)
- {
- UINTPTR intSave;
- intSave = LOS_IntLock();
- g_losTaskLock++;
- LOS_IntRestore(intSave);
- }
3.3.2 解鎖任務調度LOS_TaskUnlock()
我們看看解鎖任務調度函數LOS_TaskUnlock(),⑴處如果任務鎖調度計數器全局變量數值大于0,對其減1。⑵處如果任務鎖調度計數器等于0,則執行⑶處觸發調度。代碼如下:
- LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskUnlock(VOID)
- {
- UINTPTR intSave;
- intSave = LOS_IntLock();
- ⑴ if (g_losTaskLock > 0) {
- g_losTaskLock--;
- ⑵ if (g_losTaskLock == 0) {
- LOS_IntRestore(intSave);
- ⑶ LOS_Schedule();
- return;
- }
- }
- LOS_IntRestore(intSave);
- }
3.4 控制任務優先級
LiteOS-M內核支持動態設置任務的優先級,提供了一些操作。
3.4.1 設置指定任務的優先級LOS_TaskPriSet
支持設置指定任務Id的優先級,也支持對當前運行任務進行優先級設置。⑴處開始,做些基礎校驗,包含檢驗傳入的優先級參數taskPrio,指定任務的Id,任務是否未創建等,如果沒有通過參數校驗,則返回錯誤碼。⑵處調用函數設置任務優先級,稍后分析該函數。如果任務處于就緒狀態或者運行狀態,則會執行⑶主動觸發任務調度。
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskPriSet(UINT32 taskID, UINT16 taskPrio)
- {
- BOOL isReady = FALSE;
- UINTPTR intSave;
- LosTaskCB *taskCB = NULL;
- UINT16 tempStatus;
- ⑴ if (taskPrio > OS_TASK_PRIORITY_LOWEST) {
- return LOS_ERRNO_TSK_PRIOR_ERROR;
- }
- if (taskID == g_idleTaskID) {
- return LOS_ERRNO_TSK_OPERATE_IDLE;
- }
- if (taskID == g_swtmrTaskID) {
- return LOS_ERRNO_TSK_OPERATE_SWTMR;
- }
- if (OS_CHECK_TSK_PID_NOIDLE(taskID)) {
- return LOS_ERRNO_TSK_ID_INVALID;
- }
- taskCB = OS_TCB_FROM_TID(taskID);
- intSave = LOS_IntLock();
- tempStatus = taskCB->taskStatus;
- if (tempStatus & OS_TASK_STATUS_UNUSED) {
- LOS_IntRestore(intSave);
- return LOS_ERRNO_TSK_NOT_CREATED;
- }
- ⑵ isReady = OsSchedModifyTaskSchedParam(taskCB, taskPrio);
- LOS_IntRestore(intSave);
- if (isReady) {
- ⑶ LOS_Schedule();
- }
- return LOS_OK;
- }
接下來,我們分析下函數OsSchedModifyTaskSchedParam()。⑴處如果任務處于就緒狀態,需要先出隊設置優先級,然后入隊就緒隊列。如果非就緒狀態,可以直接執行⑵處語句修改任務優先級。如果任務正在運行,需要返回TRUE,標記下需要任務調度。
- BOOL OsSchedModifyTaskSchedParam(LosTaskCB *taskCB, UINT16 priority)
- {
- if (taskCB->taskStatus & OS_TASK_STATUS_READY) {
- ⑴ OsSchedTaskDeQueue(taskCB);
- taskCB->priority = priority;
- OsSchedTaskEnQueue(taskCB);
- return TRUE;
- }
- ⑵ taskCB->priority = priority;
- OsHookCall(LOS_HOOK_TYPE_TASK_PRIMODIFY, taskCB, taskCB->priority);
- if (taskCB->taskStatus & OS_TASK_STATUS_RUNNING) {
- return TRUE;
- }
- return FALSE;
- }
3.4.2 獲取指定任務的優先級LOS_TaskPriGet
獲取指定任務的優先級LOS_TaskPriGet()代碼比較簡單,⑴處如果任務編號無效,返回錯誤碼。⑵處如果任務未創建返回錯誤碼。如果參數校驗通過,執行⑶獲取任務的優先級數值。
- LITE_OS_SEC_TEXT_MINOR UINT16 LOS_TaskPriGet(UINT32 taskID)
- {
- UINTPTR intSave;
- LosTaskCB *taskCB = NULL;
- UINT16 priority;
- ⑴ if (OS_CHECK_TSK_PID_NOIDLE(taskID)) {
- return (UINT16)OS_INVALID;
- }
- taskCB = OS_TCB_FROM_TID(taskID);
- intSave = LOS_IntLock();
- ⑵ if (taskCB->taskStatus & OS_TASK_STATUS_UNUSED) {
- LOS_IntRestore(intSave);
- return (UINT16)OS_INVALID;
- }
- ⑶ priority = taskCB->priority;
- LOS_IntRestore(intSave);
- return priority;
- }
3.5 任務阻塞和喚醒
最后,我們分析下函數OsSchedTaskWait()和OsSchedTaskWake(),這2個函數定義在文件kernel\src\los_sched.c中。任務在申請互斥鎖、信號量、出入隊列、讀寫事件時,都可能導致任務進入阻塞狀態,對應地也需要任務喚醒重新進入就緒隊列狀態。這2個函數就負責任務的阻塞和喚醒,我們分析下他們的代碼。
3.5.1 任務阻塞
我們分析下任務阻塞的函數OsSchedTaskWait(),需要2個參數:LOS_DL_LIST *list是互斥鎖等資源的阻塞鏈表,阻塞的任務會掛這個鏈表里;UINT32 ticks是任務阻塞的時間。分析下具體代碼:
⑴獲取正在請求互斥鎖等資源的當前任務,⑵設置任務狀態為阻塞狀態。⑶把任務插入互斥鎖等資源的阻塞鏈表的尾部。⑷如果不是永久阻塞等待,任務的狀態還需要設置為:
- VOID OsSchedTaskWait(LOS_DL_LIST *list, UINT32 ticks)
- {
- ⑴ LosTaskCB *runTask = g_losTask.runTask;
- ⑵ runTask->taskStatus |= OS_TASK_STATUS_PEND;
- ⑶ LOS_ListTailInsert(list, &runTask->pendList);
- if (ticks != LOS_WAIT_FOREVER) {
- ⑷ runTask->taskStatus |= OS_TASK_STATUS_PEND_TIME;
- runTask->waitTimes = ticks;
- }
- }
3.5.2 任務喚醒
我們分析下任務喚醒的函數OsSchedTaskWake(),需要1個參數:LosTaskCB *resumedTask是需要喚醒的任務;任務喚醒函數會從阻塞鏈表里刪除并加入就緒隊列,下面分析下具體代碼:
⑴把要喚醒的任務從所在的阻塞隊列中刪除,然后更改狀態不再為阻塞狀態。⑵如果任務不是永久等待,需要從定時器排序鏈表中刪除,并設置狀態不再是等待超時。⑶如果任務是阻塞狀態,改為就緒狀態并加入就緒隊列。
- VOID OsSchedTaskWake(LosTaskCB *resumedTask)
- {
- ⑴ LOS_ListDelete(&resumedTask->pendList);
- resumedTask->taskStatus &= ~OS_TASK_STATUS_PEND;
- ⑵ if (resumedTask->taskStatus & OS_TASK_STATUS_PEND_TIME) {
- OsDeleteSortLink(&resumedTask->sortList, OS_SORT_LINK_TASK);
- resumedTask->taskStatus &= ~OS_TASK_STATUS_PEND_TIME;
- }
- ⑶ if (!(resumedTask->taskStatus & OS_TASK_STATUS_SUSPEND)) {
- OsSchedTaskEnQueue(resumedTask);
- }
- }
小結
本文帶領大家一起剖析了鴻蒙輕內核任務模塊的源代碼,包含任務模塊的結構體,任務初始化過程源代碼,任務常用操作的源代碼。后續也會陸續推出更多的分享文章,敬請期待。