玖叶教程网

前端编程开发入门

LiteOS内核源码分析系列六-任务调度LOS_Task11

2、调度模块常用接口

这一小节,我们看看kernel\base\sched\sched_sq\los_sched.c定义的调度接口,包含VOID OsSchedPreempt(VOID)VOID OsSchedResched(VOID)两个主要的调度接口。两者的区别是,前者需要把当前任务放入就绪队列内,再调用后者触发调用。后者直接从就绪队列里获取下一个任务,然后触发调度去运行下一个任务。这2个接口都是内部接口,对外提供的调度接口是上一小节分析过的STATIC INLINE VOID LOS_Schedule(VOID),三者有调用关系STATIC INLINE VOID LOS_Schedule(VOID)--->VOID OsSchedPreempt(VOID)--->VOID OsSchedResched(VOID)

我们分析下这些调度接口的源代码。

2.1 抢占调度函数VOID OsSchedResched(VOID)

抢占调度函数VOID OsSchedResched(VOID),我们分析下源代码。

⑴验证需要持有任务模块的自旋锁。⑵处判断是否支持调度,如果不具备调度的条件,则暂不调度。⑶获取当前运行任务,从就绪队列中获取下一个高优先级的任务。验证下一个任务newTask不能为空,并更改其状态为非就绪状态。⑷处判断当前任务和下一个任务不能为同一个,否则返回。这种情况不会发生,当前任务肯定会从优先级队列中移除的,二者不可能是同一个。⑸更改2个任务的运行状态,当前任务设置为非运行状态,下一个任务设置为运行状态。⑹处如果支持多核,则更改任务的运行在哪个核。紧接着的一些代码属于调度维测信息,暂时不管。⑺处如果支持时间片调度,并且下一个新任务的时间片为0,设置为时间片超时时间的最大值LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT。⑻设置下一个任务newTask为当前运行任务,会更新全局变量g_runTask。然后调用汇编函数OsTaskSchedule(newTask, runTask)执行调度,后文分析该汇编函数的实现代码。

VOID OsSchedResched(VOID)
{
    LosTaskCB *runTask = NULL;
    LosTaskCB *newTask = NULL;

⑴  LOS_ASSERT(LOS_SpinHeld(&g_taskSpin));

⑵  if (!OsPreemptableInSched()) {
        return;
    }

⑶  runTask = OsCurrTaskGet();
    newTask = OsGetTopTask();
    LOS_ASSERT(newTask != NULL);
    newTask->taskStatus &= ~OS_TASK_STATUS_READY;

⑷  if (runTask == newTask) {
        return;
    }

⑸  runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
    newTask->taskStatus |= OS_TASK_STATUS_RUNNING;

#ifdef LOSCFG_KERNEL_SMP
⑹  runTask->currCpu = OS_TASK_INVALID_CPUID;
    newTask->currCpu = ArchCurrCpuid();
#endif

    OsTaskTimeUpdateHook(runTask->taskId, LOS_TickCountGet());

#ifdef LOSCFG_KERNEL_CPUP
    OsTaskCycleEndStart(newTask);
#endif

#ifdef LOSCFG_BASE_CORE_TSK_MONITOR
    OsTaskSwitchCheck(runTask, newTask);
#endif

    LOS_TRACE(TASK_SWITCH, newTask->taskId, runTask->priority, runTask->taskStatus, newTask->priority,
        newTask->taskStatus);

#ifdef LOSCFG_DEBUG_SCHED_STATISTICS
    OsSchedStatistics(runTask, newTask);
#endif

    PRINT_TRACE("cpu%u (%s) status: %x -> (%s) status:%x\n", ArchCurrCpuid(),
                runTask->taskName, runTask->taskStatus,
                newTask->taskName, newTask->taskStatus);

#ifdef LOSCFG_BASE_CORE_TIMESLICE
    if (newTask->timeSlice == 0) {
⑺      newTask->timeSlice = LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT;
    }
#endif

⑻  OsCurrTaskSet((VOID*)newTask);
    OsTaskSchedule(newTask, runTask);
}

2.2 抢占调度函数VOID OsSchedPreempt(VOID)

抢占调度函数VOID OsSchedPreempt(VOID),把当前任务放入就绪队列,从队列中获取高优先级任务,然后尝试调度。当锁调度,或者没有更高优先级任务时,调度不会发生。⑴处判断是否支持调度,如果不具备调度的条件,则暂不调度。⑵获取当前任务,更改其状态为非就绪状态。

如果开启时间片调度并且当前任务时间片为0,则执行⑶把当前任务放入就绪队列的尾部,否则执行⑷把当前任务放入就绪队列的头部,同等优先级下可以更早的运行。⑸调用函数OsSchedResched()去调度。

VOID OsSchedPreempt(VOID)
{
    LosTaskCB *runTask = NULL;
    UINT32 intSave;

⑴  if (!OsPreemptable()) {
        return;
    }

    SCHEDULER_LOCK(intSave);

⑵  runTask = OsCurrTaskGet();
    runTask->taskStatus |= OS_TASK_STATUS_READY;

#ifdef LOSCFG_BASE_CORE_TIMESLICE
    if (runTask->timeSlice == 0) {
⑶      OsPriQueueEnqueue(&runTask->pendList, runTask->priority);
    } else {
#endif
⑷      OsPriQueueEnqueueHead(&runTask->pendList, runTask->priority);
#ifdef LOSCFG_BASE_CORE_TIMESLICE
    }
#endif

⑸  OsSchedResched();

    SCHEDULER_UNLOCK(intSave);
}

2.3 时间片检查函数VOID OsTimesliceCheck(VOID)

函数VOID OsTimesliceCheck(VOID)在支持时间片调度时才生效,该函数在tick中断函数VOID OsTickHandler(VOID)里调用。如果当前运行函数的时间片使用完毕,则触发调度。⑴处获取当前运行任务,⑵判断runTask->timeSlice时间片是否为0,不为0则减1。如果减1后为0,则执行⑶调用LOS_Schedule()触发调度。

#ifdef LOSCFG_BASE_CORE_TIMESLICE
LITE_OS_SEC_TEXT VOID OsTimesliceCheck(VOID)
{
⑴  LosTaskCB *runTask = OsCurrTaskGet();
⑵  if (runTask->timeSlice != 0) {
        runTask->timeSlice--;
        if (runTask->timeSlice == 0) {
⑶          LOS_Schedule();
        }
    }
}
#endif

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言