鴻蒙輕內(nèi)核M核源碼分析系列六任務(wù)及任務(wù)調(diào)度(2)任務(wù)模塊
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
任務(wù)是操作系統(tǒng)一個(gè)重要的概念,是競(jìng)爭(zhēng)系統(tǒng)資源的最小運(yùn)行單元。任務(wù)可以使用或等待CPU、使用內(nèi)存空間等系統(tǒng)資源,并獨(dú)立于其它任務(wù)運(yùn)行。鴻蒙輕內(nèi)核的任務(wù)模塊可以給用戶提供多個(gè)任務(wù),實(shí)現(xiàn)任務(wù)間的切換,幫助用戶管理業(yè)務(wù)程序流程。
接下來,我們看下任務(wù)模塊的結(jié)構(gòu)體,任務(wù)初始化,任務(wù)常用操作的源代碼。
1、任務(wù)模塊的結(jié)構(gòu)體定義
在文件kernel\include\los_task.h定義的任務(wù)控制塊結(jié)構(gòu)體LosTaskCB,源代碼如下,結(jié)構(gòu)體成員的解釋見注釋部分。
- typedef struct {
- VOID *stackPointer; /* 任務(wù)棧指針 */
- UINT16 taskStatus; /* 任務(wù)狀態(tài) */
- UINT16 priority; /* 任務(wù)優(yōu)先級(jí) */
- INT32 timeSlice; /* 剩余的時(shí)間片 */
- UINT32 waitTimes;
- SortLinkList sortList; /* 任務(wù)超時(shí)排序鏈表節(jié)點(diǎn) */
- UINT64 startTime;
- UINT32 stackSize; /* 任務(wù)棧大小 */
- UINT32 topOfStack; /* 棧頂指針 */
- UINT32 taskID; /* 任務(wù)編號(hào)Id */
- TSK_ENTRY_FUNC taskEntry; /* 任務(wù)入口函數(shù) */
- VOID *taskSem; /* 任務(wù)持有的信號(hào)量 */
- VOID *taskMux; /* 導(dǎo)致任務(wù)阻塞的互斥鎖 */
- UINT32 arg; /* 任務(wù)入口函數(shù)的參數(shù) */
- CHAR *taskName; /* 任務(wù)名稱 */
- LOS_DL_LIST pendList; /* 就緒隊(duì)列等鏈表節(jié)點(diǎn) */
- LOS_DL_LIST timerList; /* 任務(wù)超時(shí)排序鏈表節(jié)點(diǎn) */
- EVENT_CB_S event;
- UINT32 eventMask; /* 事件掩碼 */
- UINT32 eventMode; /* 事件模式 */
- VOID *msg; /* 分給給隊(duì)列的內(nèi)存*/
- INT32 errorNo;
- } LosTaskCB;
另外一個(gè)比較重要的結(jié)構(gòu)體是TSK_INIT_PARAM_S,創(chuàng)建任務(wù)時(shí),需要指定任務(wù)初始化的參數(shù)。源代碼如下,結(jié)構(gòu)體成員的解釋見注釋部分。
- typedef struct tagTskInitParam {
- TSK_ENTRY_FUNC pfnTaskEntry; /** 任務(wù)入口函數(shù) */
- UINT16 usTaskPrio; /** 任務(wù)參數(shù) */
- UINT32 uwStackSize; /** 任務(wù)棧大小 */
- CHAR *pcName; /** 任務(wù)名稱 */
- UINT32 uwResved; /** 保留 */
- } TSK_INIT_PARAM_S;
2、任務(wù)模塊初始化
在系統(tǒng)啟動(dòng)時(shí),在kernel\src\los_init.c中調(diào)用OsTaskInit()進(jìn)行任務(wù)模塊初始化,還會(huì)調(diào)用OsIdleTaskCreate()創(chuàng)建空閑任務(wù)。
2.1 任務(wù)模塊初始化
函數(shù)OsTaskInit()定義在kernel\src\los_task.c,我們分析下這個(gè)函數(shù)的執(zhí)行過程。
⑴處代碼根據(jù)開發(fā)板配置的最大任務(wù)數(shù)g_taskMaxNum,計(jì)算需要申請(qǐng)的內(nèi)存大小size,為任務(wù)控制塊TCB數(shù)組(也叫作任務(wù)池)g_taskCBArray申請(qǐng)內(nèi)存。為什么比最大任務(wù)數(shù)多申請(qǐng)一個(gè)呢?在刪除任務(wù)時(shí)會(huì)使用。下文分析刪除任務(wù)的源碼時(shí)再詳細(xì)講解其用意。
⑵處代碼初始化雙向鏈表g_losFreeTask用作空閑的任務(wù)鏈表、g_taskRecyleList可以回收的任務(wù)鏈表。
⑶處循環(huán)初始化每一個(gè)任務(wù),任務(wù)狀態(tài)未使用OS_TASK_STATUS_UNUSED,初始化任務(wù)Id,并把任務(wù)掛在空閑任務(wù)鏈表上。
⑷處初始化全局變量LosTask g_losTask,該全局變量維護(hù)當(dāng)前運(yùn)行的任務(wù)和要調(diào)度執(zhí)行的任務(wù)。初始化任務(wù)池時(shí),設(shè)置當(dāng)前運(yùn)行的任務(wù)為g_taskCBArray[g_taskMaxNum]。⑸處空閑任務(wù)編號(hào)暫時(shí)設(shè)置為無效值,后續(xù)創(chuàng)建空閑任務(wù)時(shí)再設(shè)置空閑任務(wù)編號(hào)。
優(yōu)先級(jí)隊(duì)列,詳細(xì)的代碼實(shí)現(xiàn)剖析,參見之前的源碼剖析文章。⑸處互斥鎖死鎖檢測(cè)的調(diào)測(cè)特性的,后續(xù)系列文章專題進(jìn)行講解。⑹處代碼初始化排序鏈表,詳細(xì)的代碼實(shí)現(xiàn)剖析,參見之前的源碼剖析文章。⑺處如果開啟了惰性棧,計(jì)算TCB的成員變量stackFrame在其結(jié)構(gòu)體中的偏移量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 創(chuàng)建空閑任務(wù)IdleCore000
除了初始化任務(wù)池,在系統(tǒng)啟動(dòng)階段還會(huì)創(chuàng)建idle空閑任務(wù)。⑴處設(shè)置任務(wù)初始化參數(shù)時(shí),空閑任務(wù)的入口執(zhí)行函數(shù)為OsIdleTask()。⑵處調(diào)用函數(shù)把空閑任務(wù)狀態(tài)設(shè)置為就緒狀態(tài)。
- 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;
- }
我們看下空閑任務(wù)的入口執(zhí)行函數(shù)為OsIdleTask(),它調(diào)用OsRecyleFinishedTask()回收任務(wù)棧資源,后文會(huì)分析如何回收任務(wù)資源。
- LITE_OS_SEC_TEXT WEAK VOID OsIdleTask(VOID)
- {
- while (1) {
- OsRecyleFinishedTask();
- HalEnterSleep(OS_SYS_DEEP_SLEEP);
- }
- }
3、任務(wù)模塊常用操作
3.1 創(chuàng)建和刪除任務(wù)
3.1.1 創(chuàng)建任務(wù)
鴻蒙輕內(nèi)核提供了2個(gè)創(chuàng)建任務(wù)的函數(shù),有LOS_TaskCreate、LOS_TaskCreateOnly。LOS_TaskCreate和LOS_TaskCreateOnly的區(qū)別是,前者創(chuàng)建任務(wù)完畢就使任務(wù)進(jìn)入就緒狀態(tài),并觸發(fā)調(diào)度,如果就緒隊(duì)列中沒有更高優(yōu)先級(jí)的任務(wù),則運(yùn)行該任務(wù)。后者只創(chuàng)建任務(wù),設(shè)置任務(wù)狀態(tài)為阻塞suspend狀態(tài),需要開發(fā)者去調(diào)用LOS_TaskResume使該任務(wù)進(jìn)入ready狀態(tài)。
函數(shù)LOS_TaskCreate代碼如下,可以看出創(chuàng)建任務(wù)的時(shí)候,調(diào)用⑴處的函數(shù)LOS_TaskCreateOnly()來創(chuàng)建任務(wù)。創(chuàng)建任務(wù)后,執(zhí)行⑵處的代碼使任務(wù)進(jìn)入ready就緒隊(duì)列,如果系統(tǒng)啟動(dòng)完成,允許任務(wù)調(diào)度,則執(zhí)行⑶觸發(fā)任務(wù)調(diào)度。如果新創(chuàng)建的任務(wù)優(yōu)先級(jí)最高,則會(huì)被調(diào)度運(yùn)行。
- 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;
- }
我們接著分析下如何使用函數(shù)UINT32 LOS_TaskCreateOnly()創(chuàng)建任務(wù)。⑴處調(diào)用OsTaskInitParamCheck()檢測(cè)創(chuàng)建任務(wù)的參數(shù)的合法性。⑵處調(diào)用函數(shù)回收釋放的任務(wù)。⑶處如果任務(wù)池為空,無法創(chuàng)建任務(wù),返回錯(cuò)誤碼。⑷處從任務(wù)池獲取一個(gè)空閑的任務(wù)控制塊taskCB,然后從空閑任務(wù)鏈表中刪除。⑸處根據(jù)指定的任務(wù)棧大小為任務(wù)棧申請(qǐng)內(nèi)存,⑹處判斷任務(wù)棧內(nèi)存申請(qǐng)釋放成功,如果申請(qǐng)失敗,則把任務(wù)控制塊歸還到空閑任務(wù)鏈表中,并返回錯(cuò)誤碼。⑺處調(diào)用函數(shù)初始化任務(wù)棧,更新任務(wù)控制塊成員信息。詳細(xì)見后面對(duì)該函數(shù)的分析。
- 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;
- }
我們看下創(chuàng)建任務(wù)函數(shù)調(diào)用的函數(shù)OsRecyleFinishedTask(),該函數(shù)在系統(tǒng)進(jìn)入空閑時(shí)也會(huì)調(diào)用。刪除運(yùn)行狀態(tài)的任務(wù)時(shí),會(huì)把任務(wù)掛在雙向鏈表里g_taskRecyleList。任務(wù)回收函數(shù)就用來回收此類任務(wù),實(shí)現(xiàn)任務(wù)資源回收。我們分析下它的代碼。⑴處循環(huán)遍歷回收鏈表,⑵從回收鏈表獲取第一個(gè)任務(wù)taskCB,從回收鏈表刪除并插入到空閑任務(wù)鏈表里。任務(wù)棧保護(hù)在后續(xù)系列再深入分析,繼續(xù)往下看代碼,⑶處獲取任務(wù)棧棧頂指針,接著調(diào)用內(nèi)存釋放函數(shù)來釋放任務(wù)棧占用的內(nèi)存,并設(shè)置任務(wù)棧的棧頂為空。
- 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);
- }
我們繼續(xù)分析下函數(shù)OsNewTaskInit(),⑴處調(diào)用函數(shù)初始化任務(wù)棧,上一系列已經(jīng)分析過該函數(shù),代碼的其余部分用來更新任務(wù)控制塊的成員信息,比如⑵處任務(wù)狀態(tài)設(shè)置為阻塞狀態(tài)。
- 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 刪除任務(wù)UINT32 LOS_TaskDelete()
該函數(shù)根據(jù)傳入的參數(shù)UINT32 taskId刪除任務(wù)。我們分析下刪除任務(wù)的源代碼,⑴處檢驗(yàn)傳入的參數(shù),⑵處如果任務(wù)還未創(chuàng)建,返回錯(cuò)誤碼。⑶處如果刪除的任務(wù)正在運(yùn)行,又處于鎖任務(wù)調(diào)度情況下,打印信息,告訴用戶不推薦在鎖任務(wù)調(diào)度期間進(jìn)行任務(wù)刪除,然后執(zhí)行⑷,把全局變量賦值0來解鎖任務(wù)調(diào)度。
⑸處調(diào)用函數(shù)處理任務(wù)狀態(tài),如果處于就緒狀態(tài)設(shè)置為非就緒狀態(tài),并從就緒隊(duì)列刪除。如果處于阻塞狀態(tài),從阻塞隊(duì)列中刪除。如果任務(wù)處于超時(shí)等待狀態(tài),從超時(shí)排序鏈表中刪除。⑹恢復(fù)任務(wù)控制塊事件相關(guān)的成員信息。⑺如果任務(wù)正在運(yùn)行,設(shè)置任務(wù)為未使用狀態(tài),接著調(diào)用函數(shù)OsRunningTaskDelete()把任務(wù)放入回收鏈表,然后主動(dòng)觸發(fā)任務(wù)調(diào)度,稍后詳細(xì)分析該函數(shù)。如果刪除的任務(wù)不是出于運(yùn)行狀態(tài),則執(zhí)行⑻,設(shè)置任務(wù)為未使用狀態(tài),接著把任務(wù)回收到空閑任務(wù)鏈表里,然后獲取任務(wù)棧的棧頂指針,調(diào)用內(nèi)存釋放函數(shù)釋放任務(wù)棧的內(nèi)存。
- 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;
- }
我們看下函數(shù)OsRunningTaskDelete()的源碼。⑴處把當(dāng)前運(yùn)行的任務(wù)放入待回收鏈表里,然后執(zhí)行⑵把當(dāng)前運(yùn)行的任務(wù)放入任務(wù)池的最后一個(gè)位置g_taskCBArray[g_taskMaxNum]。為什么這么操作呢?等后續(xù)分析源碼的時(shí)候再來解答。
- 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 控制任務(wù)狀態(tài)
3.2.1 恢復(fù)掛起的任務(wù)LOS_TaskResume()
恢復(fù)掛起的任務(wù),使該任務(wù)進(jìn)入就緒狀態(tài),和下文中的LOS_TaskSuspend()成對(duì)使用。⑴處獲取任務(wù)的TCB,⑵處對(duì)任務(wù)狀態(tài)進(jìn)行判斷,如果任務(wù)未創(chuàng)建或者非阻塞狀態(tài),則返回錯(cuò)誤碼。執(zhí)行⑶設(shè)置任務(wù)狀態(tài)為非掛起狀態(tài)。⑶處獲取任務(wù)的狀態(tài)進(jìn)行判斷,如果任務(wù)沒有創(chuàng)建或者不是掛起狀態(tài),則返回相應(yīng)的錯(cuò)誤碼。 ⑷檢查任務(wù)狀態(tài)是否為OS_CHECK_TASK_BLOCK,即(OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND | OS_TASK_STATUS_SUSPEND)中的一種,這幾個(gè)狀態(tài)影響恢復(fù)掛起的任務(wù)。如果非上述幾個(gè)狀態(tài),執(zhí)行⑸調(diào)用函數(shù),把任務(wù)狀態(tài)改為就緒狀態(tài),插入任務(wù)就緒隊(duì)列。如果支持支持調(diào)度,則執(zhí)行⑹觸發(fā)調(diào)度。
- 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 掛起指定的任務(wù)LOS_TaskSuspend()
函數(shù)用于掛起指定的任務(wù)。⑴處獲取任務(wù)的TCB,⑵處開始獲取任務(wù)的狀態(tài)進(jìn)行判斷,如果任務(wù)沒有創(chuàng)建、任務(wù)已經(jīng)掛起,返回相應(yīng)的錯(cuò)誤碼。⑶處如果任務(wù)是運(yùn)行狀態(tài),并且鎖任務(wù)調(diào)度時(shí),跳轉(zhuǎn)到LOS_ERREND結(jié)束掛起操作。⑷處如果任務(wù)是就緒狀態(tài),調(diào)用函數(shù)從就緒隊(duì)列出隊(duì),并取消任務(wù)的就緒狀態(tài)。⑸處語句設(shè)置任務(wù)狀態(tài)為阻塞狀態(tài)。⑹如果掛起的是當(dāng)前運(yùn)行的任務(wù),則會(huì)主動(dòng)觸發(fā)調(diào)度。
- 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 任務(wù)延時(shí)等待LOS_TaskDelay()
任務(wù)延時(shí)等待,釋放CPU,等待時(shí)間到期后該任務(wù)會(huì)重新進(jìn)入就緒狀態(tài)。
⑴處代碼判斷系統(tǒng)處于中斷,如果是,則返回錯(cuò)誤碼,不允許任務(wù)延時(shí)等待。
⑵如果處于鎖任務(wù)調(diào)度期間,則返回錯(cuò)誤碼。
⑶處如果延遲的時(shí)間為0,則執(zhí)行讓權(quán)操作,否則執(zhí)行。
⑷調(diào)用函數(shù)OsSchedDelay()把當(dāng)前任務(wù)設(shè)置為延時(shí)等待狀態(tài),然后調(diào)用LOS_Schedule()觸發(fā)調(diào)度。
- 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;
- }
另外還提供了函數(shù)LOS_Msleep()和LOS_UDelay(),前者以毫秒為單位進(jìn)行延遲等待。后者也是以毫秒為單位進(jìn)行延遲等待,但是不會(huì)觸發(fā)任務(wù)調(diào)度,當(dāng)前任務(wù)不會(huì)釋放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 任務(wù)讓權(quán)LOS_TaskYield()
讓權(quán)函數(shù)通過把當(dāng)前任務(wù)時(shí)間片設(shè)置為0,釋放CPU占用,重新調(diào)度給其他高優(yōu)先級(jí)任務(wù)執(zhí)行。⑴處調(diào)用函數(shù)把當(dāng)前任務(wù)時(shí)間片設(shè)置為0,然后執(zhí)行⑵主動(dòng)觸發(fā)任務(wù)調(diào)度。
- LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskYield(VOID)
- {
- UINTPTR intSave;
- intSave = LOS_IntLock();
- ⑴ OsSchedYield();
- LOS_IntRestore(intSave);
- ⑵ LOS_Schedule();
- return LOS_OK;
- }
接下來看下函數(shù)OsSchedYield()的源碼。代碼很簡(jiǎn)單,獲取當(dāng)前運(yùn)行的任務(wù),然后把其時(shí)間片設(shè)置為0,如下:
- VOID OsSchedYield(VOID)
- {
- LosTaskCB *runTask = g_losTask.runTask;
- runTask->timeSlice = 0;
- }
3.3 控制任務(wù)調(diào)度
3.3.1 鎖任務(wù)調(diào)度LOS_TaskLock()
鎖任務(wù)調(diào)度LOS_TaskLock()比較簡(jiǎn)單,把任務(wù)鎖調(diào)度計(jì)數(shù)器全局變量增加1即可,代碼如下。
- LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskLock(VOID)
- {
- UINTPTR intSave;
- intSave = LOS_IntLock();
- g_losTaskLock++;
- LOS_IntRestore(intSave);
- }
3.3.2 解鎖任務(wù)調(diào)度LOS_TaskUnlock()
我們看看解鎖任務(wù)調(diào)度函數(shù)LOS_TaskUnlock(),⑴處如果任務(wù)鎖調(diào)度計(jì)數(shù)器全局變量數(shù)值大于0,對(duì)其減1。⑵處如果任務(wù)鎖調(diào)度計(jì)數(shù)器等于0,則執(zhí)行⑶處觸發(fā)調(diào)度。代碼如下:
- 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 控制任務(wù)優(yōu)先級(jí)
LiteOS-M內(nèi)核支持動(dòng)態(tài)設(shè)置任務(wù)的優(yōu)先級(jí),提供了一些操作。
3.4.1 設(shè)置指定任務(wù)的優(yōu)先級(jí)LOS_TaskPriSet
支持設(shè)置指定任務(wù)Id的優(yōu)先級(jí),也支持對(duì)當(dāng)前運(yùn)行任務(wù)進(jìn)行優(yōu)先級(jí)設(shè)置。⑴處開始,做些基礎(chǔ)校驗(yàn),包含檢驗(yàn)傳入的優(yōu)先級(jí)參數(shù)taskPrio,指定任務(wù)的Id,任務(wù)是否未創(chuàng)建等,如果沒有通過參數(shù)校驗(yàn),則返回錯(cuò)誤碼。⑵處調(diào)用函數(shù)設(shè)置任務(wù)優(yōu)先級(jí),稍后分析該函數(shù)。如果任務(wù)處于就緒狀態(tài)或者運(yùn)行狀態(tài),則會(huì)執(zhí)行⑶主動(dòng)觸發(fā)任務(wù)調(diào)度。
- 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;
- }
接下來,我們分析下函數(shù)OsSchedModifyTaskSchedParam()。⑴處如果任務(wù)處于就緒狀態(tài),需要先出隊(duì)設(shè)置優(yōu)先級(jí),然后入隊(duì)就緒隊(duì)列。如果非就緒狀態(tài),可以直接執(zhí)行⑵處語句修改任務(wù)優(yōu)先級(jí)。如果任務(wù)正在運(yùn)行,需要返回TRUE,標(biāo)記下需要任務(wù)調(diào)度。
- 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 獲取指定任務(wù)的優(yōu)先級(jí)LOS_TaskPriGet
獲取指定任務(wù)的優(yōu)先級(jí)LOS_TaskPriGet()代碼比較簡(jiǎn)單,⑴處如果任務(wù)編號(hào)無效,返回錯(cuò)誤碼。⑵處如果任務(wù)未創(chuàng)建返回錯(cuò)誤碼。如果參數(shù)校驗(yàn)通過,執(zhí)行⑶獲取任務(wù)的優(yōu)先級(jí)數(shù)值。
- 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 任務(wù)阻塞和喚醒
最后,我們分析下函數(shù)OsSchedTaskWait()和OsSchedTaskWake(),這2個(gè)函數(shù)定義在文件kernel\src\los_sched.c中。任務(wù)在申請(qǐng)互斥鎖、信號(hào)量、出入隊(duì)列、讀寫事件時(shí),都可能導(dǎo)致任務(wù)進(jìn)入阻塞狀態(tài),對(duì)應(yīng)地也需要任務(wù)喚醒重新進(jìn)入就緒隊(duì)列狀態(tài)。這2個(gè)函數(shù)就負(fù)責(zé)任務(wù)的阻塞和喚醒,我們分析下他們的代碼。
3.5.1 任務(wù)阻塞
我們分析下任務(wù)阻塞的函數(shù)OsSchedTaskWait(),需要2個(gè)參數(shù):LOS_DL_LIST *list是互斥鎖等資源的阻塞鏈表,阻塞的任務(wù)會(huì)掛這個(gè)鏈表里;UINT32 ticks是任務(wù)阻塞的時(shí)間。分析下具體代碼:
⑴獲取正在請(qǐng)求互斥鎖等資源的當(dāng)前任務(wù),⑵設(shè)置任務(wù)狀態(tài)為阻塞狀態(tài)。⑶把任務(wù)插入互斥鎖等資源的阻塞鏈表的尾部。⑷如果不是永久阻塞等待,任務(wù)的狀態(tài)還需要設(shè)置為:
- 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 任務(wù)喚醒
我們分析下任務(wù)喚醒的函數(shù)OsSchedTaskWake(),需要1個(gè)參數(shù):LosTaskCB *resumedTask是需要喚醒的任務(wù);任務(wù)喚醒函數(shù)會(huì)從阻塞鏈表里刪除并加入就緒隊(duì)列,下面分析下具體代碼:
⑴把要喚醒的任務(wù)從所在的阻塞隊(duì)列中刪除,然后更改狀態(tài)不再為阻塞狀態(tài)。⑵如果任務(wù)不是永久等待,需要從定時(shí)器排序鏈表中刪除,并設(shè)置狀態(tài)不再是等待超時(shí)。⑶如果任務(wù)是阻塞狀態(tài),改為就緒狀態(tài)并加入就緒隊(duì)列。
- 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);
- }
- }
小結(jié)
本文帶領(lǐng)大家一起剖析了鴻蒙輕內(nèi)核任務(wù)模塊的源代碼,包含任務(wù)模塊的結(jié)構(gòu)體,任務(wù)初始化過程源代碼,任務(wù)常用操作的源代碼。后續(xù)也會(huì)陸續(xù)推出更多的分享文章,敬請(qǐng)期待。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)