LiteOS内核源码分析系列六 -任务及调度(4)-任务LOS_Task
3.4 控制任务优先级
LiteOS
支持动态设置任务的优先级,提供了一些操作。
3.4.1 设置指定任务的优先级LOS_TaskPriSet
支持设置指定任务Id
的优先级,也支持对当前运行任务进行优先级设置。⑴处开始,做些基础校验,包含检验传入的优先级参数taskPrio
,指定任务的Id
,任务是否未创建等,任务是否系统任务等。⑵处获取任务就绪状态,如果在就绪队列里,需要先出队设置优先级然后入队就绪队列。⑶如果任务不在就绪队列,可以直接设置任务的优先级。如果任务正在运行,标记下需要调度。⑷如果对正在运行的任务更改的优先级,触发一次任务调度。
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskPriSet(UINT32 taskId, UINT16 taskPrio)
{
BOOL isReady = FALSE;
UINT32 intSave;
LosTaskCB *taskCB = NULL;
UINT16 tempStatus;
⑴ if (taskPrio > OS_TASK_PRIORITY_LOWEST) {
return LOS_ERRNO_TSK_PRIOR_ERROR;
}
if (OS_TASK_ID_CHECK_INVALID(taskId)) {
return LOS_ERRNO_TSK_ID_INVALID;
}
taskCB = OS_TCB_FROM_TID(taskId);
if (taskCB->taskFlags & OS_TASK_FLAG_SYSTEM) {
return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
}
SCHEDULER_LOCK(intSave);
tempStatus = taskCB->taskStatus;
if (tempStatus & OS_TASK_STATUS_UNUSED) {
SCHEDULER_UNLOCK(intSave);
return LOS_ERRNO_TSK_NOT_CREATED;
}
isReady = tempStatus & OS_TASK_STATUS_READY;
if (isReady) {
⑵ OsPriQueueDequeue(&taskCB->pendList);
taskCB->priority = taskPrio;
OsPriQueueEnqueue(&taskCB->pendList, taskCB->priority);
} else {
⑶ taskCB->priority = taskPrio;
if (tempStatus & OS_TASK_STATUS_RUNNING) {
isReady = TRUE;
}
}
SCHEDULER_UNLOCK(intSave);
LOS_TRACE(TASK_PRIOSET, taskCB->taskId, taskCB->taskStatus, taskCB->priority, taskPrio);
⑷ if (isReady) {
LOS_MpSchedule(OS_MP_CPU_ALL);
LOS_Schedule();
}
return LOS_OK;
}
3.4.2 获取指定任务的优先级LOS_TaskPriGet
获取指定任务的优先级LOS_TaskPriGet()
代码比较简单,如果任务未创建返回错误码。否则返回任务的优先级数值。
LITE_OS_SEC_TEXT_MINOR UINT16 LOS_TaskPriGet(UINT32 taskId)
{
UINT32 intSave;
LosTaskCB *taskCB = NULL;
UINT16 priority;
if (OS_TASK_ID_CHECK_INVALID(taskId)) {
return (UINT16)OS_INVALID;
}
taskCB = OS_TCB_FROM_TID(taskId);
SCHEDULER_LOCK(intSave);
if (taskCB->taskStatus & OS_TASK_STATUS_UNUSED) {
SCHEDULER_UNLOCK(intSave);
return (UINT16)OS_INVALID;
}
priority = taskCB->priority;
SCHEDULER_UNLOCK(intSave);
return priority;
}
3.5 设置和获取任务亲和性
对于SMP
多核系统,任务模块支持设置任务运行在哪些核上。
3.5.1 设置指定任务的运行cpu集合LOS_TaskCpuAffiSet
该函数仅在SMP
模式下支持,我们分析下其代码。⑴处LOSCFG_KERNEL_CPU_MASK
是CPU
核掩码,有几个核该数值二进制就有几个1,最多支持16个核。检查传入的参数cpuAffiMask
是否合法。⑵处根据传入的参数taskId
获取任务taskCB
。接着,如果是系统任务或者任务没有创建,返回错误码。
⑶处设置任务运行在哪些CPU
上,然后获取任务当前运行的CPU
。如果当前运行的CPU
和刚设置完毕的任务运行在的指定CPU
不一致,则标记需要调度。⑷处如果需要调度,并且当前支持任务调度时,触发调度。
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskCpuAffiSet(UINT32 taskId, UINT16 cpuAffiMask)
{
#ifdef LOSCFG_KERNEL_SMP
LosTaskCB *taskCB = NULL;
UINT32 intSave;
BOOL needSched = FALSE;
UINT16 currCpuMask;
if (OS_TASK_ID_CHECK_INVALID(taskId)) {
return LOS_ERRNO_TSK_ID_INVALID;
}
⑴ if (!(cpuAffiMask & LOSCFG_KERNEL_CPU_MASK)) {
return LOS_ERRNO_TSK_CPU_AFFINITY_MASK_ERR;
}
⑵ taskCB = OS_TCB_FROM_TID(taskId);
if (taskCB->taskFlags & OS_TASK_FLAG_SYSTEM) {
return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
}
SCHEDULER_LOCK(intSave);
if (taskCB->taskStatus & OS_TASK_STATUS_UNUSED) {
SCHEDULER_UNLOCK(intSave);
return LOS_ERRNO_TSK_NOT_CREATED;
}
⑶ taskCB->cpuAffiMask = cpuAffiMask;
currCpuMask = CPUID_TO_AFFI_MASK(taskCB->currCpu);
if (!(currCpuMask & cpuAffiMask)) {
needSched = TRUE;
taskCB->signal = SIGNAL_AFFI;
}
SCHEDULER_UNLOCK(intSave);
⑷ if (needSched && OS_SCHEDULER_ACTIVE) {
LOS_MpSchedule(currCpuMask);
LOS_Schedule();
}
#endif
(VOID)taskId;
(VOID)cpuAffiMask;
return LOS_OK;
}
3.5.2 获取指定任务的运行cpu集合LOS_TaskCpuAffiGet
该函数获取指定任务的运行cpu集合,代码比较简单。⑴处如果任务没有创建,返回错误码。⑵处获取任务的运行CPU
集合并返回。
LITE_OS_SEC_TEXT_MINOR UINT16 LOS_TaskCpuAffiGet(UINT32 taskId)
{
#ifdef LOSCFG_KERNEL_SMP
#define INVALID_CPU_AFFI_MASK 0
LosTaskCB *taskCB = NULL;
UINT16 cpuAffiMask;
UINT32 intSave;
if (OS_TASK_ID_CHECK_INVALID(taskId)) {
return INVALID_CPU_AFFI_MASK;
}
taskCB = OS_TCB_FROM_TID(taskId);
SCHEDULER_LOCK(intSave);
⑴ if (taskCB->taskStatus & OS_TASK_STATUS_UNUSED) {
SCHEDULER_UNLOCK(intSave);
return INVALID_CPU_AFFI_MASK;
}
⑵ cpuAffiMask = taskCB->cpuAffiMask;
SCHEDULER_UNLOCK(intSave);
return cpuAffiMask;
#else
(VOID)taskId;
return 1; /* 1: mask of current cpu */
#endif
}
3.6 回收任务栈资源
从上文已经知道,系统空闲时会调用VOID LOS_TaskResRecycle(VOID)
函数回收任务栈资源,该函数调用函数VOID OsTaskCBRecycleToFree(VOID)
实现任务资源回收。我们分析下它的代码。
3.6.1 回收所有待回收的任务栈资源LOS_TaskResRecycle
任务删除时,会把任务挂在双向链表里g_taskRecycleList
。⑴处循环遍历回收链表,⑵从回收链表获取第一个任务taskCB
,从回收链表删除并插入到空闲任务链表里。任务栈保护、异常交互在后续系列再深入分析,继续往下看代码,⑶处释放任务栈占用的内存,并设置任务的栈顶为空。
LITE_OS_SEC_TEXT_INIT STATIC VOID OsTaskCBRecycleToFree(VOID)
{
LosTaskCB *taskCB = NULL;
VOID *poolTmp = NULL;
#ifdef LOSCFG_TASK_STACK_PROTECT
UINTPTR MMUProtectAddr;
#endif
⑴ while (!LOS_ListEmpty(&g_taskRecycleList)) {
poolTmp = (VOID *)m_aucSysMem1;
⑵ taskCB = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&g_taskRecycleList));
LOS_ListDelete(LOS_DL_LIST_FIRST(&g_taskRecycleList));
LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
#ifdef LOSCFG_TASK_STACK_PROTECT
MMUProtectAddr = taskCB->topOfStack - MMU_4K;
OsTaskStackProtect(MMUProtectAddr, MMU_4K, ACCESS_PERM_RW_RW);
#ifdef LOSCFG_EXC_INTERACTION
if (MMUProtectAddr < (UINTPTR)m_aucSysMem1) {
poolTmp = (VOID *)m_aucSysMem0;
}
#endif
(VOID)LOS_MemFree(poolTmp, (VOID *)MMUProtectAddr);
#else
#ifdef LOSCFG_EXC_INTERACTION
if (taskCB->topOfStack < (UINTPTR)m_aucSysMem1) {
poolTmp = (VOID *)m_aucSysMem0;
}
#endif
⑶ (VOID)LOS_MemFree(poolTmp, (VOID *)taskCB->topOfStack);
#endif
taskCB->topOfStack = 0;
}
}
VOID LOS_TaskResRecycle(VOID)
{
UINT32 intSave;
SCHEDULER_LOCK(intSave);
OsTaskCBRecycleToFree();
SCHEDULER_UNLOCK(intSave);
}
3.7 任务阻塞和唤醒
最后,我们分析下函数OsTaskWait()
和OsTaskWake()
,任务在申请互斥锁、信号量、出入队列、读写事件时,都可能导致任务进入阻塞状态,对应地也需要任务唤醒重新进入就绪队列状态。这2个函数就负责任务的阻塞和唤醒,我们分析下他们的代码。
3.7.1 任务阻塞
我们分析下任务阻塞的函数OsTaskWait()
,需要3个参数:LOS_DL_LIST *list
是互斥锁等资源的阻塞链表,阻塞的任务会挂这个链表里;UINT16 taskStatus
是需要设置阻塞的任务的状态,一般为OS_TASK_STATUS_PEND
;UINT32 timeout
是任务阻塞的时间。分析下具体代码:
⑴获取正在请求互斥锁等资源的当前任务,更改其优先级,不再是就绪状态。⑵设置任务的阻塞状态。⑶把任务插入互斥锁等资源的阻塞链表的尾部。⑷如果不是永久等待,任务的状态还需要设置为OS_TASK_STATUS_PEND_TIME
,然后把任务加入定时器排序链表里。
VOID OsTaskWait(LOS_DL_LIST *list, UINT16 taskStatus, UINT32 timeout)
{
LosTaskCB *runTask = NULL;
LOS_DL_LIST *pendObj = NULL;
⑴ runTask = OsCurrTaskGet();
runTask->taskStatus &= ~OS_TASK_STATUS_READY;
pendObj = &runTask->pendList;
⑵ runTask->taskStatus |= taskStatus;
⑶ LOS_ListTailInsert(list, pendObj);
if (timeout != LOS_WAIT_FOREVER) {
⑷ runTask->taskStatus |= OS_TASK_STATUS_PEND_TIME;
OsTaskAdd2TimerList((LosTaskCB *)runTask, timeout);
}
}
3.7.2 任务唤醒
我们分析下任务唤醒的函数OsTaskWake()
,需要2个参数:LosTaskCB *resumedTask
是需要唤醒的任务;UINT16 taskStatus
是需要设置阻塞的任务的状态,一般为OS_TASK_STATUS_PEND
;任务唤醒函数会从阻塞链表里删除并加入就绪队列,下面分析下具体代码:
⑴把要唤醒的任务从所在的阻塞队列中删除,然后更改状态不再为阻塞状态。⑵如果任务不是永久等待,需要从定时器排序链表中删除。⑶如果任务是阻塞状态,改为就绪状态并加入就绪队列。
VOID OsTaskWake(LosTaskCB *resumedTask, UINT16 taskStatus)
{
⑴ LOS_ListDelete(&resumedTask->pendList);
resumedTask->taskStatus &= ~taskStatus;
⑵ if (resumedTask->taskStatus & OS_TASK_STATUS_PEND_TIME) {
OsTimerListDelete(resumedTask);
resumedTask->taskStatus &= ~OS_TASK_STATUS_PEND_TIME;
}
⑶ if (!(resumedTask->taskStatus & OS_TASK_STATUS_SUSPEND)) {
resumedTask->taskStatus |= OS_TASK_STATUS_READY;
OsPriQueueEnqueue(&resumedTask->pendList, resumedTask->priority);
}
}
小结
本文带领大家一起剖析了LiteOS
任务模块的源代码,包含任务模块的结构体,任务初始化过程源代码,任务常用操作的源代码。
感谢阅读,如有任何问题、建议,都可以留言给我们: https://gitee.com/LiteOS/LiteOS/issues 。为了更容易找到LiteOS
代码仓,建议访问 https://gitee.com/LiteOS/LiteOS ,关注Watch
、点赞Star
、并Fork
到自己账户下,如下图,谢谢。
- 点赞
- 收藏
- 关注作者
评论(0)