鴻蒙輕內核M核源碼分析系列六 任務及任務調度(2)任務模塊

任務是操作系統一個重要的概念,是競爭系統資源的最小運行單元。任務可以使用或等待CPU、使用內存空間等系統資源,并獨立于其它任務運行。鴻蒙輕內核的任務模塊可以給用戶提供多個任務,實現任務間的切換,幫助用戶管理業務程序流程。本文我們來一起學習下任務模塊的源代碼,所涉及的源碼,以OpenHarmony LiteOS-M內核為例,均可以在開源站點 https://gitee.com/openharmony/kernel_liteos_m 獲取。

接下來,我們看下任務模塊的結構體,任務初始化,任務常用操作的源代碼。

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_TaskCreateLOS_TaskCreateOnlyLOS_TaskCreateLOS_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));
#endifif (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;
#elsestackPtr = 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是任務阻塞的時間。分析下具體代碼:

⑴獲取正在請求互斥鎖等資源的當前任務,⑵設置任務狀態為阻塞狀態。⑶把任務插入互斥鎖等資源的阻塞鏈表的尾部。⑷如果不是永久阻塞等待,任務的狀態還需要設置為OS_TASK_STATUS_PEND_TIME,然后設置任務的等待時間為傳入的參數。

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);}
}

小結

本文帶領大家一起剖析了鴻蒙輕內核任務模塊的源代碼,包含任務模塊的結構體,任務初始化過程源代碼,任務常用操作的源代碼。

如果大家想更加深入的學習 OpenHarmony 開發的內容,不妨可以參考以下相關學習文檔進行學習,助你快速提升自己:

OpenHarmony 開發環境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源碼解析》:https://qr18.cn/CgxrRy

  • 搭建開發環境
  • Windows 開發環境的搭建
  • Ubuntu 開發環境搭建
  • Linux 與 Windows 之間的文件共享
  • ……

系統架構分析:https://qr18.cn/CgxrRy

  • 構建子系統
  • 啟動流程
  • 子系統
  • 分布式任務調度子系統
  • 分布式通信子系統
  • 驅動子系統
  • ……

OpenHarmony 設備開發學習手冊:https://qr18.cn/CgxrRy

在這里插入圖片描述

OpenHarmony面試題(內含參考答案):https://qr18.cn/CgxrRy

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:
http://www.pswp.cn/web/22757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/22757.shtml
英文地址,請注明出處:http://en.pswp.cn/web/22757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

智慧校園究竟有何魅力?

隨著科技的快速發展&#xff0c;智慧校園已成為教育領域的熱門話題。智慧校園利用先進的技術手段&#xff0c;將信息化與教育深度融合&#xff0c;為學生、教師和家長提供更便捷、高效的教育服務。本文將帶您深入了解智慧校園的魅力&#xff0c;讓您對未來教育的發展充滿期待。…

Ego微商項目部署(小程序項目)(全網最詳細教程)

目錄 1.項目部署前的準備 1.1獲取APPID和APPSecret&#xff08;微信小程序&#xff09; 1.2測試工具 1.3微信開發者工具下載與安裝 2.Ego微商后端項目部署 2.1部署細節流程 2.2部署架構圖 2.3組件要求及版本 2.4后臺部署操作 2.4.1安裝vm和cenos7 2.4.2本地服務檢查…

我們如何利用 0 美元營銷將 UX/UI 產品發展到 320k 用戶

嘿 &#x1f44b; 我是 Paul&#xff0c;FlowMapp 的聯合創始人。 現在&#xff0c;我們是一個由7人&#xff08;少數兼職成員&#xff09;組成的團隊&#xff0c;試圖將產品擴展到$ 1M ARR。 希望這些對您有所幫助&#xff0c;并祝您未來的產品好運&#xff01; 我決定與…

【AI大模型】Transformers大模型庫(四):AutoTokenizer

目錄??????? 一、引言 二、自動分詞器&#xff08;AutoTokenizer&#xff09; 2.1 概述 2.2 主要特點 2.3 代碼示例 三、總結 一、引言 這里的Transformers指的是huggingface開發的大模型庫&#xff0c;為huggingface上數以萬計的預訓練大模型提供預測、訓練等服…

PMAT安裝及使用(Bioinformatics工具-021)

01 背景 PMAT 是一個高效的組裝工具包&#xff0c;用于利用第三代&#xff08;HiFi/CLR/ONT&#xff09;測序數據組裝植物線粒體基因組。PMAT 還可以用于組裝葉綠體基因組或動物線粒體基因組。 PMAT&#xff1a;使用低覆蓋度HiFi測序數據的高效植物線粒體組裝工具包-文獻精讀…

python字符串的進階

在上一篇文章的 密碼破解器 中&#xff0c;我們回顧了循環專題的知識點。 while 循環和 for 循環是 Python 中的兩大循環語句&#xff0c;它們都可以實現循環的功能&#xff0c;但在具體使用時略有差別。當循環次數不確定時&#xff0c;我們選用 while 循環&#xff1b;當循環…

【限免】雜波環境下線性調頻脈沖、巴克碼、頻率步進脈沖雷達MTI、脈沖壓縮【附MATLAB代碼】

文章來源&#xff1a;?微信公眾號&#xff1a;EW Frontier/ 智能電磁頻譜算法 本代碼主要模擬雜波環境&#xff08;飛機、地雜波、鳥類信號&#xff09;下&#xff0c;Chirp脈沖、巴克碼脈沖、頻率步進脈沖雷達信號的脈沖壓縮及MTI、匹配濾波。 MATLAB主代碼 % 生成雷達信號…

做任務賺錢的app有哪些?(真實可靠能做任務賺錢軟件app推薦)

在數字化時代&#xff0c;通過手機APP做任務賺錢已成為一種流行的兼職方式。這些APP為用戶提供了完成小任務以賺取現金或獎勵的機會。以下是一些真實可靠的做任務賺錢的APP推薦&#xff0c;幫助您在空閑時間增加收入。 賞幫賺是一個正規的兼職接單賺錢平臺&#xff0c;在這個平…

MariaDB數據導入與導出操作演示

文章目錄 整個數據庫導出導入先刪除庫然后再導入 參考這里&#xff1a; MariaDB數據庫導出導入. 整個數據庫 該部分演示&#xff1a;導出數據庫&#xff0c;然后重建數據庫&#xff0c;并導入數據的整個過程。 導出 Win R &#xff0c;打開運行輸入cmd并回車&#xff0c;然…

迅雷極簡易下載

一、簡介 1、迅雷是一家全球領先的去中心化服務商&#xff0c;以技術構建商業&#xff0c;以服務創造共識&#xff0c;從而建立一個高效可信的存儲與傳輸網絡。 迅雷成立于2003年&#xff0c;總部位于中國深圳&#xff0c;2014年于納斯達克上市&#xff08;納斯達克股票代碼&a…

Linux系統管理磁盤管理003

操作系統&#xff1a; CentOS Stream9 測試過程&#xff1a; 模擬磁盤被沾滿&#xff0c; 創建文件 測試腳本 for i in seq 10do# echo $idd if/dev/zero of./$i-$RANDOM.txt bs1M count1024 Done[rootlocalhost ~]# vim 2.txt [rootlocalhost ~]# sh 2.txt 記錄了10240 的…

OPPO 文件傳輸 - 將文件從 OPPO 手機傳輸到 PC 的 5 種方法

OPPO手機以其出色的拍照功能而聞名&#xff0c;尤其是新推出的OPPO Find X2系列&#xff0c;它配備了高清前置鏡頭和超夜景模式&#xff0c;讓您輕松拍出精彩瞬間。當您需要將這些照片或其他文件從OPPO手機傳輸到PC時&#xff0c;以下是五種簡便的方法。 第 1 部分&#xff…

UI設計公司-藍藍設計-交通行業ui設計解決方案

來百度APP暢享高清圖片 這是北京蘭亭妙微科技有限公司&#xff08;簡稱藍藍設計&#xff09;在交通行業的一些ui設計經驗&#xff0c;我們建立了UI設計分享群&#xff0c;每天會分享國內外的一些優秀設計&#xff0c;如果有興趣的話&#xff0c;可以進入一起成長學習&#xff0…

電路方案分析(十九)快速響應過流事件檢測電路

快速響應過流事件檢測電路 1.設計需求2.設計方案3.設計說明4.仿真驗證 tips&#xff1a;方案參考來自TI參考設計&#xff0c;僅供學習交流使用。 1.設計需求 2.設計方案 這是一種快速響應單向電流檢測解決方案&#xff0c;通常稱為過流保護 (OCP)&#xff0c;可提供 < 2μ…

【AI大模型】基于Langchain和Openai借口實現英文翻譯中文應用

&#x1f680; 作者 &#xff1a;“大數據小禪” &#x1f680; 文章簡介 &#xff1a;本專欄后續將持續更新大模型相關文章&#xff0c;從開發到微調到應用&#xff0c;需要下載好的模型包可私。 &#x1f680; 歡迎小伙伴們 點贊&#x1f44d;、收藏?、留言&#x1f4ac; 目…

【python009】Python處理某區域邊界經緯度數據至geohash

1.熟悉、梳理、總結項目研發實戰中的Python開發日常使用中的問題、知識點等&#xff0c;如Python處理某區域邊界經緯度數據至geohash&#xff0c;便于時空交集。 2.歡迎點贊、關注、批評、指正&#xff0c;互三走起來&#xff0c;小手動起來&#xff01; 3.歡迎點贊、關注、批評…

net/http與gin框架的關系分析

要想學好 gin 框架&#xff0c;首先要學習 net/http 服務&#xff0c;而二者的關系又是重中之重。 本文所要做的任務就是將二者“連接” 起來&#xff0c;讓讀者掌握其中之精髓。 一、Golang HTTP 標準庫示例 使用 golang 啟動 http 服務非常簡單&#xff0c;就是一個標準的 C…

【數據庫初階】SQL--DCL

文章目錄 DCL1. 基本介紹2. 用戶管理2.1 查詢用戶2.2 創建用戶2.3 修改用戶密碼2.4 刪除用戶 3. 權限控制3.1 查詢權限3.2 授予權限3.3 撤銷權限 4. DCL總結 DCL 更多數據庫MySQL系統內容就在以下專欄&#xff1a; 專欄鏈接&#xff1a;數據庫MySQL 1. 基本介紹 DCL英文全稱是…

45-3 護網溯源 - 為什么要做溯源工作

官網:CVERC-國家計算機病毒應急處理中心 西工大遭網絡攻擊再曝細節!13名攻擊者身份查明→ (baidu.com) 護網溯源是指通過技術手段追蹤網絡攻擊的來源和行為,其重要性體現在以下幾個方面: 安全防御:了解攻擊源頭可以幫助組織加強網絡安全防御,及時采取措施防止攻擊的再次…

NXP i.MX8系列平臺開發講解 - 3.14 Linux 之Power Supply子系統(二)

專欄文章目錄傳送門&#xff1a;返回專欄目錄 Hi, 我是你們的老朋友&#xff0c;主要專注于嵌入式軟件開發&#xff0c;有興趣不要忘記點擊關注【碼思途遠】 目錄 1. 前言 2. 芯片簡介 2. 系統原理設計 2. 設備樹相關 本文實操是基于Android11 系統下i.MX8MQ環境下&#x…