LiteOS内核源码分析系列六 -任务及调度(3)-任务LOS_Task
3、任务模块常用操作
3.1 创建和删除任务
3.1.1 创建任务
LiteOS
提供了4个创建任务的函数,有LOS_TaskCreate
、LOS_TaskCreateOnly
、LOS_TaskCreateStatic
、LOS_TaskCreateOnlyStatic
。LOS_TaskCreate
和LOS_TaskCreateOnly
的区别是,前者创建任务完毕就使任务进入就绪状态,并触发调度,如果就绪队列中没有更高优先级的任务,则运行该任务。后者只创建任务,设置任务状态为阻塞suspend
状态,需要开发者去调用LOS_TaskResume
使该任务进入ready状态。
LOS_TaskCreateStatic
和LOS_TaskCreateOnlyStatic
的区别同上,是否自动去调度。LOS_TaskCreate
、LOS_TaskCreateOnly
和LOS_TaskCreateStatic
、LOS_TaskCreateOnlyStatic
的区别是,创建任务时,任务栈由系统创建还是使用用户提供的任务栈。这4个函数都会调用OsTaskCreateOnly
来创建函数,我们以函数LOS_TaskCreate
为例一起剖析下创建任务的源码。
函数LOS_TaskCreate
代码如下,可以看出创建任务的时候,调用⑴处的函数OsTaskCreateOnly(taskId, initParam, NULL, FALSE)
,其中FALSE
表示不使用用户传入的栈。如果创建精通分配用户栈的任务,需要传递用户栈顶指针、TRUE
2个参数,如OsTaskCreateOnly(taskId, initParam, topStack, TRUE)
。创建任务后,执行⑶处的代码使任务进行ready
就绪状态,下文分别详细分析下这2个函数。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreateOnly(UINT32 *taskId, TSK_INIT_PARAM_S *initParam)
{
⑴ return OsTaskCreateOnly(taskId, initParam, NULL, FALSE);
}
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskCreate(UINT32 *taskId, TSK_INIT_PARAM_S *initParam)
{
UINT32 ret;
⑵ ret = LOS_TaskCreateOnly(taskId, initParam);
if (ret != LOS_OK) {
return ret;
}
⑶ OsTaskResume(taskId);
return LOS_OK;
}
我们接着分析下如何使用函数UINT32 OsTaskCreateOnly()
创建任务,参数BOOL useUsrStack
此时取值为FALSE
。⑴处调用OsTaskCreateParamCheck(taskId, initParam, &pool)
检测创建任务的参数的合法性。⑵处代码调用OsTaskGetFreeTaskCB(&taskCB)
从g_losFreeTask
空闲任务链表中获取一个可用的taskCB
。⑶处调用OsTaskSyncCreate(taskCB)
,只有开启SMP
模式、开启LOSCFG_KERNEL_SMP_TASK_SYNC
宏时,才支持多任务同步操作,我们先忽略这个方法的实现。⑷处代码不使用用户栈时,调用函数OsTaskStackAlloc()
,申请内存,栈顶为topStack
。⑸处代码调用OsTaskStackInit()
初始化任务栈,函数返回值是栈指针地址stackPtr
,⑹调用函数OsTaskCBInit()
初始化任务TCB
,然后更新taskId
,完成任务的创建。OsTaskStackInit()
、OsTaskCBInit()
的具体实现在下文分析。⑺创建任务成功后,返回任务Id
。
STATIC UINT32 OsTaskCreateOnly(UINT32 *taskId, TSK_INIT_PARAM_S *initParam, VOID *topStack, BOOL useUsrStack)
{
UINT32 intSave, errRet;
VOID *stackPtr = NULL;
LosTaskCB *taskCB = NULL;
VOID *pool = NULL;
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
if (useUsrStack) {
errRet = OsTaskCreateParamCheckStatic(taskId, initParam, topStack);
} else {
#endif
⑴ errRet = OsTaskCreateParamCheck(taskId, initParam, &pool);
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
}
#endif
if (errRet != LOS_OK) {
return errRet;
}
SCHEDULER_LOCK(intSave);
⑵ errRet = OsTaskGetFreeTaskCB(&taskCB);
if (errRet != LOS_OK) {
OS_GOTO_ERREND();
}
SCHEDULER_UNLOCK(intSave);
⑶ errRet = OsTaskSyncCreate(taskCB);
if (errRet != LOS_OK) {
goto LOS_ERREND_REWIND_TCB;
}
⑷ if (useUsrStack == FALSE) {
OsTaskStackAlloc(&topStack, initParam->uwStackSize, pool);
if (topStack == NULL) {
errRet = LOS_ERRNO_TSK_NO_MEMORY;
goto LOS_ERREND_REWIND_SYNC;
}
}
⑸ stackPtr = OsTaskStackInit(taskCB->taskId, initParam->uwStackSize, topStack);
⑹ OsTaskCBInit(taskCB, initParam, stackPtr, topStack, useUsrStack);
if (OsConsoleIDSetHook != NULL) {
OsConsoleIDSetHook(taskCB->taskId, OsCurrTaskGet()->taskId);
}
#ifdef LOSCFG_KERNEL_CPUP
OsCpupCB *cpup = OsCpupCBGet(taskCB->taskId);
cpup->id = taskCB->taskId;
cpup->status = taskCB->taskStatus;
#endif
⑺ *taskId = taskCB->taskId;
return LOS_OK;
LOS_ERREND_REWIND_SYNC:
OsTaskSyncDestroy(taskCB);
LOS_ERREND_REWIND_TCB:
SCHEDULER_LOCK(intSave);
LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
LOS_ERREND:
SCHEDULER_UNLOCK(intSave);
return errRet;
}
最后,来看看函数VOID OsTaskResume(const UINT32 *taskId)
,⑴处把任务的状态从挂起状态``OS_TASK_STATUS_SUSPEND改为就绪状态
OS_TASK_STATUS_READY,⑵处代码先判断是否支持调度,支持调度则调用
LOS_Schedule()`函数调度,如果就绪队列中没有更高优先级的任务,则运行该新创建的任务。
STATIC VOID OsTaskResume(const UINT32 *taskId)
{
UINT32 intSave;
LosTaskCB *taskCB = NULL;
taskCB = OS_TCB_FROM_TID(*taskId);
SCHEDULER_LOCK(intSave);
⑴ taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;
taskCB->taskStatus |= OS_TASK_STATUS_READY;
OsPriQueueEnqueue(&taskCB->pendList, taskCB->priority);
SCHEDULER_UNLOCK(intSave);
LOS_TRACE(TASK_CREATE, taskCB->taskId, taskCB->taskStatus, taskCB->priority);
LOS_MpSchedule(OS_MP_CPU_ALL);
if (OS_SCHEDULER_ACTIVE) {
⑵ LOS_Schedule();
}
}
3.1.2 删除任务UINT32 LOS_TaskDelete()
该函数根据传入的参数UINT32 taskId
删除任务。我们分析下删除任务的源代码,⑴处检验传入的参数,然后获取任务taskCB
。如果是系统任务,或者任务未创建,返回错误码。⑵处如果删除的是运行中的任务,需要调用函数OsTaskDeleteCheckOnRun(taskCB, &errRet)
判断是否允许删除,该函数做如下判断:
- 开启
SMP
多核时,不允许跨核删除运行状态的任务 - 不允许调度时,不能删除运行状态的任务
- 中断处理期间,不能删除运行状态的任务
⑶处如果任务处于就绪队列中,着从就绪队列出队,并设置状态为非就绪状态。如果处于阻塞状态,从阻塞队列中删除。⑷如果任务处于超时等待状态,从超时排序链表中删除。⑸处设置任务状态非阻塞,未创建。如果开启LOSCFG_BASE_IPC_EVENT
、LOSCFG_LAZY_STACK
宏,重置相关的信息。⑹处调用函数OsTaskDelAction(taskCB, taskCB->usrStack)
执行删除任务操作。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskDelete(UINT32 taskId)
{
LosTaskCB *taskCB = NULL;
UINT32 intSave, errRet;
UINT16 tempStatus;
⑴ 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) {
errRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
}
⑵ if ((tempStatus & OS_TASK_STATUS_RUNNING) &&
!OsTaskDeleteCheckOnRun(taskCB, &errRet)) {
OS_GOTO_ERREND();
}
⑶ if (tempStatus & OS_TASK_STATUS_READY) {
OsPriQueueDequeue(&taskCB->pendList);
taskCB->taskStatus &= ~OS_TASK_STATUS_READY;
} else if (tempStatus & OS_TASK_STATUS_PEND) {
LOS_ListDelete(&taskCB->pendList);
}
⑷ if (tempStatus & (OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND_TIME)) {
OsTimerListDelete(taskCB);
}
⑸ taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;
taskCB->taskStatus |= OS_TASK_STATUS_UNUSED;
#ifdef LOSCFG_BASE_IPC_EVENT
taskCB->event.uwEventID = OS_INVALID_VALUE;
taskCB->eventMask = 0;
#endif
#ifdef LOSCFG_LAZY_STACK
taskCB->stackFrame = 0;
#endif
#ifdef LOSCFG_KERNEL_CPUP
(VOID)memset_s((VOID *)OsCpupCBGet(taskCB->taskId), sizeof(OsCpupCB), 0, sizeof(OsCpupCB));
#endif
OS_MEM_CLEAR(taskId);
OsTaskSyncWake(taskCB);
⑹ if (OsTaskDelAction(taskCB, taskCB->usrStack)) {
OsSchedResched();
}
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
LOS_ERREND:
SCHEDULER_UNLOCK(intSave);
return errRet;
}
接下来,我们分析下负责删除任务的核心函数BOOL OsTaskDelAction(LosTaskCB *taskCB, BOOL useUsrStack)
。⑴先处理下运行状态的任务的删除。⑵如果支持静态分配任务并且使用用户栈,则直接把任务加入空闲任务链表g_losFreeTask
,否则把任务加入待回收列表g_losFreeTask
,等执行空闲任务时再回收。⑶处的函数会把待删除任务的信息备份到任务池的最后g_taskCBArray[g_taskMaxNum],然后把任务taskCB
的状态设置为未使用。
⑷处删除非运行状态的任务,把任务taskCB
的状态设置为未使用,然后把任务加入空闲任务链表g_losFreeTask
。⑸处如果使用的不是用户栈,需要释放任务栈使用的内存空间。LOSCFG_TASK_STACK_PROTECT
宏只有部分平台支持,可以忽略。或者任务栈顶指针,然后执行⑹释放内存,完成任务删除。
LITE_OS_SEC_TEXT_INIT STATIC BOOL OsTaskDelAction(LosTaskCB *taskCB, BOOL useUsrStack)
{
VOID *pool = (VOID *)m_aucSysMem1;
UINTPTR taskStack;
LOS_TRACE(TASK_DELETE, taskCB->taskId, taskCB->taskStatus, taskCB->usrStack);
⑴ if (taskCB->taskStatus & OS_TASK_STATUS_RUNNING) {
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
⑵ if (useUsrStack) {
LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
} else {
#endif
LOS_ListTailInsert(&g_taskRecycleList, &taskCB->pendList);
#ifdef LOSCFG_TASK_STATIC_ALLOCATION
}
#endif
⑶ OsTaskDelActionOnRun(taskCB);
return TRUE;
}
⑷ taskCB->taskStatus = OS_TASK_STATUS_UNUSED;
LOS_ListAdd(&g_losFreeTask, &taskCB->pendList);
⑸ if (useUsrStack == FALSE) {
#ifdef LOSCFG_TASK_STACK_PROTECT
taskStack = taskCB->topOfStack - MMU_4K;
OsTaskStackProtect(taskStack, MMU_4K, ACCESS_PERM_RW_RW);
#else
taskStack = taskCB->topOfStack;
#endif
#ifdef LOSCFG_EXC_INTERACTION
if (taskStack < (UINTPTR)m_aucSysMem1) {
pool = (VOID *)m_aucSysMem0;
}
#endif
⑹ (VOID)LOS_MemFree(pool, (VOID *)taskStack);
}
taskCB->topOfStack = 0;
return FALSE;
}
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)
中的一种,这几个状态影响恢复挂起的任务。⑹把任务状态改为就绪状态,入队就绪队列,并标记是否需要调度needSched = TRUE
。⑺如果需要调度,则触发调度。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskResume(UINT32 taskId)
{
UINT32 intSave;
LosTaskCB *taskCB = NULL;
UINT16 tempStatus;
UINT32 errRet;
BOOL needSched = FALSE;
if (OS_TASK_ID_CHECK_INVALID(taskId)) {
return LOS_ERRNO_TSK_ID_INVALID;
}
⑴ taskCB = OS_TCB_FROM_TID(taskId);
SCHEDULER_LOCK(intSave);
/* clear pending signal */
⑵ taskCB->signal &= ~SIGNAL_SUSPEND;
⑶ tempStatus = taskCB->taskStatus;
if (tempStatus & OS_TASK_STATUS_UNUSED) {
errRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
} else if (!(tempStatus & OS_TASK_STATUS_SUSPEND)) {
errRet = LOS_ERRNO_TSK_NOT_SUSPENDED;
OS_GOTO_ERREND();
}
⑷ taskCB->taskStatus &= ~OS_TASK_STATUS_SUSPEND;
⑸ if (!(taskCB->taskStatus & OS_CHECK_TASK_BLOCK)) {
⑹ taskCB->taskStatus |= OS_TASK_STATUS_READY;
OsPriQueueEnqueue(&taskCB->pendList, taskCB->priority);
if (OS_SCHEDULER_ACTIVE) {
needSched = TRUE;
}
}
SCHEDULER_UNLOCK(intSave);
LOS_TRACE(TASK_RESUME, taskCB->taskId, taskCB->taskStatus, taskCB->priority);
if (needSched) {
⑺ LOS_MpSchedule(OS_MP_CPU_ALL);
LOS_Schedule();
}
return LOS_OK;
LOS_ERREND:
SCHEDULER_UNLOCK(intSave);
return errRet;
}
3.2.2 挂起指定的任务LOS_TaskSuspend()
函数用于挂起指定的任务。⑴处获取任务的TCB
,如果当前任务是系统任务,返回错误码。⑵处开始获取任务的状态进行判断,如果任务没有创建、任务已经挂起,返回相应的错误码。⑶处如果任务是运行状态,并且不满足挂起任务的条件时,跳转到LOS_ERREND
结束挂起操作。BOOL OsTaskSuspendCheckOnRun(LosTaskCB *taskCB, UINT32 *ret)
可以检查运行的任务能否挂起的条件,包括:
- 开启
SMP
多核时,不允许跨核挂起运行状态的任务 - 不允许调度时,不能挂起运行状态的任务
- 中断处理期间,不能挂起运行状态的任务
⑷处如果任务是就绪状态,从就绪队列出队,并取消任务的就绪状态,下一条语句设置任务状态为阻塞状态。⑸处获取当前任务,如果挂起的是当前任务,则需要执行⑹触发调度。
LITE_OS_SEC_TEXT_INIT UINT32 LOS_TaskSuspend(UINT32 taskId)
{
UINT32 intSave;
LosTaskCB *taskCB = NULL;
LosTaskCB *runTask = NULL;
UINT16 tempStatus;
UINT32 errRet;
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) {
errRet = LOS_ERRNO_TSK_NOT_CREATED;
OS_GOTO_ERREND();
}
if (tempStatus & OS_TASK_STATUS_SUSPEND) {
errRet = LOS_ERRNO_TSK_ALREADY_SUSPENDED;
OS_GOTO_ERREND();
}
⑶ if ((tempStatus & OS_TASK_STATUS_RUNNING) &&
!OsTaskSuspendCheckOnRun(taskCB, &errRet)) {
OS_GOTO_ERREND();
}
⑷ if (tempStatus & OS_TASK_STATUS_READY) {
OsPriQueueDequeue(&taskCB->pendList);
taskCB->taskStatus &= ~OS_TASK_STATUS_READY;
}
taskCB->taskStatus |= OS_TASK_STATUS_SUSPEND;
⑸ runTask = OsCurrTaskGet();
LOS_TRACE(TASK_SUSPEND, taskCB->taskId, taskCB->taskStatus, runTask->taskId);
if (taskId == runTask->taskId) {
⑹ OsSchedResched();
}
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
LOS_ERREND:
SCHEDULER_UNLOCK(intSave);
return errRet;
}
3.2.3 任务延时等待LOS_TaskDelay()
任务延时等待,释放CPU
,等待时间到期后该任务会重新进入就绪状态。⑴处代码获取当前运行任务,如果当前任务是系统任务,则打印回溯栈并返回错误码。⑵如果不可调度,返回错误码。
⑶处如果延迟的时间为0,则执行让权操作,否则执行⑷,把当前任务取消就绪状态并加入排序链表进行等待,然后调用OsSchedResched()
触发调度。
LITE_OS_SEC_TEXT UINT32 LOS_TaskDelay(UINT32 tick)
{
UINT32 intSave;
LosTaskCB *runTask = NULL;
if (OS_INT_ACTIVE) {
return LOS_ERRNO_TSK_DELAY_IN_INT;
}
⑴ runTask = OsCurrTaskGet();
if (runTask->taskFlags & OS_TASK_FLAG_SYSTEM) {
OsBackTrace();
return LOS_ERRNO_TSK_OPERATE_SYSTEM_TASK;
}
⑵ if (!OsPreemptable()) {
return LOS_ERRNO_TSK_DELAY_IN_LOCK;
}
⑶ if (tick == 0) {
return LOS_TaskYield();
} else {
SCHEDULER_LOCK(intSave);
⑷ OsTaskAdd2TimerList(runTask, tick);
runTask->taskStatus |= OS_TASK_STATUS_DELAY;
OsSchedResched();
SCHEDULER_UNLOCK(intSave);
}
return LOS_OK;
}
3.2.4 任务让权LOS_TaskYield()
当前任务释放CPU
,并将其移到具有相同优先级的就绪任务队列的末尾。⑴处如果当前处理中断,返回错误码。⑵处判断是否可以抢占调度,如果不可调度,返回错误码。
⑶处获取当前运行中的任务runTask
,执行⑷获取当前任务优先级下的就绪任务的数量。如果数量不足,返回错误。否则执行⑸,把当前任务改为就绪状态并入队就绪队列。然后,执行⑹触发调度。
LITE_OS_SEC_TEXT_MINOR UINT32 LOS_TaskYield(VOID)
{
UINT32 tskCount;
UINT32 intSave;
LosTaskCB *runTask = NULL;
⑴ if (OS_INT_ACTIVE) {
return LOS_ERRNO_TSK_YIELD_IN_INT;
}
⑵ if (!OsPreemptable()) {
return LOS_ERRNO_TSK_YIELD_IN_LOCK;
}
⑶ runTask = OsCurrTaskGet();
if (runTask->taskId >= g_taskMaxNum) {
return LOS_ERRNO_TSK_ID_INVALID;
}
SCHEDULER_LOCK(intSave);
#ifdef LOSCFG_BASE_CORE_TIMESLICE
/* reset timeslice of yielded task */
runTask->timeSlice = 0;
#endif
⑷ tskCount = OsPriQueueSize(runTask->priority);
if (tskCount > 0) {
runTask->taskStatus |= OS_TASK_STATUS_READY;
⑸ OsPriQueueEnqueue(&(runTask->pendList), runTask->priority);
} else {
SCHEDULER_UNLOCK(intSave);
return LOS_ERRNO_TSK_YIELD_NOT_ENOUGH_TASK;
}
⑹ OsSchedResched();
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
}
3.3 控制任务调度
3.3.1 锁任务调度LOS_TaskLock()
锁任务调度LOS_TaskLock()比较简单,获取当前CPU
对应的任务锁调度计数器,然后对其增加1。
LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskLock(VOID)
{
UINT32 intSave;
UINT32 *losTaskLock = NULL;
intSave = LOS_IntLock();
losTaskLock = &OsPercpuGet()->taskLockCnt;
(*losTaskLock)++;
LOS_IntRestore(intSave);
}
3.3.2 解锁任务调度LOS_TaskUnlock()
我们看看解锁任务调度函数LOS_TaskUnlock()
,⑴处获取当前CPU
对应的任务锁调度计数器,如果数值大于0,对其减1。⑵处如果任务锁调度计数器等于0,并且等待的调度,则执行⑶处触发调度。这里,也解释了INT_PEND_RESCH
请求调度标记的用途。
LITE_OS_SEC_TEXT_MINOR VOID LOS_TaskUnlock(VOID)
{
UINT32 intSave;
UINT32 *losTaskLock = NULL;
Percpu *percpu = NULL;
intSave = LOS_IntLock();
percpu = OsPercpuGet();
⑴ losTaskLock = &percpu->taskLockCnt;
if (*losTaskLock > 0) {
(*losTaskLock)--;
⑵ if ((*losTaskLock == 0) && (percpu->schedFlag == INT_PEND_RESCH) &&
OS_SCHEDULER_ACTIVE) {
percpu->schedFlag = INT_NO_RESCH;
LOS_IntRestore(intSave);
⑶ LOS_Schedule();
return;
}
}
LOS_IntRestore(intSave);
}
- 点赞
- 收藏
- 关注作者
评论(0)