您现在的位置是:亿华云 > 系统运维
鸿蒙轻内核M核源码分析系列六任务及任务调度(2)任务模块
亿华云2025-10-03 02:13:28【系统运维】9人已围观
简介想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com任务是操作系统一个重要的概念,是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用
想了解更多内容,鸿蒙核M核源请访问:
和华为官方合作共建的轻内鸿蒙技术社区
https://harmonyos.51cto.com
任务是操作系统一个重要的概念,是码分模块竞争系统资源的最小运行单元。任务可以使用或等待CPU、析系使用内存空间等系统资源,列任并独立于其它任务运行。任务任务鸿蒙轻内核的调度任务模块可以给用户提供多个任务,实现任务间的鸿蒙核M核源切换,帮助用户管理业务程序流程。轻内
接下来,码分模块我们看下任务模块的析系结构体,任务初始化,列任任务常用操作的任务任务源代码。
1、调度任务模块的鸿蒙核M核源结构体定义
在文件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); } }小结
本文带领大家一起剖析了鸿蒙轻内核任务模块的源代码,包含任务模块的结构体,任务初始化过程源代码,任务常用操作的源代码。后续也会陆续推出更多的分享文章,敬请期待。
想了解更多内容,请访问:
和华为官方合作共建的鸿蒙技术社区
https://harmonyos.51cto.com
很赞哦!(2792)