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表示不使用用户传入的栈。如果创建精通分配用户栈的任务,需要传递用户栈顶指针、TRUE2个参数,如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;
}