做3个网站需要多大的服务器,设计兼职网站推荐,js下载wordpress,石家庄关键词优化平台任务是操作系统一个重要的概念#xff0c;是竞争系统资源的最小运行单元。任务可以使用或等待CPU、使用内存空间等系统资源#xff0c;并独立于其它任务运行。鸿蒙轻内核的任务模块可以给用户提供多个任务#xff0c;实现任务间的切换#xff0c;帮助用户管理业务程序流程。…任务是操作系统一个重要的概念是竞争系统资源的最小运行单元。任务可以使用或等待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_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));
#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