LiteOS内核源码分析系列六 -任务及调度(4)-任务LOS_Task

举报
zhushy 发表于 2021/04/01 08:17:37 2021/04/01
【摘要】 3.4 控制任务优先级LiteOS支持动态设置任务的优先级,提供了一些操作。 3.4.1 设置指定任务的优先级LOS_TaskPriSet支持设置指定任务Id的优先级,也支持对当前运行任务进行优先级设置。⑴处开始,做些基础校验,包含检验传入的优先级参数taskPrio,指定任务的Id,任务是否未创建等,任务是否系统任务等。⑵处获取任务就绪状态,如果在就绪队列里,需要先出队设置优先级然后入队...

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_MASKCPU核掩码,有几个核该数值二进制就有几个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;
#endifwhile (!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_PENDUINT32 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到自己账户下,如下图,谢谢。

LOS_STAR

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。