欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

鸿蒙轻内核M核源码分析系列六 任务及任务调度(3)任务调度模块-调度模块的重要函数

最编程 2024-06-09 07:03:21
...

文件kernel\src\los_sched.c中定义了调度模块的几个重要的函数,我们来分析下源码。

1.1 调度初始化函数

调度初始化函数UINT32 OsSchedInit(VOID)在任务初始化函数UINT32 OsTaskInit(VOID)中调用。⑴处会初始化任务就绪队列,⑵处初始化任务排序链表,⑶处初始化调度响应时间全局变量为最大值OS_SCHED_MAX_RESPONSE_TIME

UINT32 OsSchedInit(VOID)
{
    UINT16 pri;
⑴  for (pri = 0; pri < OS_PRIORITY_QUEUE_NUM; pri++) {
        LOS_ListInit(&g_priQueueList[pri]);
    }
    g_queueBitmap = 0;

⑵  g_taskSortLinkList = OsGetSortLinkAttribute(OS_SORT_LINK_TASK);
    if (g_taskSortLinkList == NULL) {
        return LOS_NOK;
    }

    OsSortLinkInit(g_taskSortLinkList);
⑶  g_schedResponseTime = OS_SCHED_MAX_RESPONSE_TIME;

    return LOS_OK;
}

1.2 任务调度函数

任务调度函数VOID LOS_Schedule(VOID)是出镜率较高的一个函数。当系统完成初始化开始调度,并且没有锁任务调度时,会调用函数HalTaskSchedule()进行任务调度。该函数定义在kernel\arch\arm\cortex-m7\gcc\los_dispatch.S,由汇编语言实现,后文会详细分析。

VOID LOS_Schedule(VOID)
{
    if (g_taskScheduled && LOS_CHECK_SCHEDULE) {
        HalTaskSchedule();
    }
}

1.3 开启调度函数

函数VOID OsSchedStart(VOID)kernel\src\los_init.c:UINT32 LOS_Start(VOID)-->kernel\arch\arm\cortex-m7\gcc\los_context.c:UINT32 HalStartSchedule(OS_TICK_HANDLER handler)函数依次调用,在系统初始化时开启任务调度。我们看下该函数的源码,⑴处调用函数获取就绪队列中优先级最高的任务,⑵把该任务状态设置为运行状态,接着把当前运行任务和新任务都设置为就绪队列中优先级最高的那个任务。⑶处设置任务调度启动状态全局变量为1,标记任务调度已经开启。⑷处设置新任务的开始运行时间,然后把新任务从就绪队列中出队。⑸处设置全局变量。⑹处调用函数设置该任务的运行过期时间。

VOID OsSchedStart(VOID)
{
    (VOID)LOS_IntLock();
⑴  LosTaskCB *newTask = OsGetTopTask();

⑵  newTask->taskStatus |= OS_TASK_STATUS_RUNNING;
    g_losTask.newTask = newTask;
    g_losTask.runTask = g_losTask.newTask;

⑶  g_taskScheduled = 1;
⑷  newTask->startTime = OsGetCurrSchedTimeCycle();
    OsSchedTaskDeQueue(newTask);

⑸  g_schedResponseTime = OS_SCHED_MAX_RESPONSE_TIME;
    g_schedResponseID = OS_INVALID;
⑹  OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, newTask->startTime + newTask->timeSlice);

    PRINTK("Entering scheduler\n");
}

1.4 任务调度切换函数

任务切换函数用于实现任务切换,被文件kernel\arch\arm\cortex-m7\gcc\los_dispatch.S中的汇编函数HalPendSV调用。我们分析下该函数的源代码。

⑴处获取当前运行的任务,然后调用函数减去其运行的时间片,开始运行时间设置为当前时间。⑵如果任务处于阻塞等待状态或延迟状态,则把其加入任务排序链表。⑶如果任务不是处于阻塞挂起状态、不是处于阻塞状态,则把其加入就绪队列。⑷处获取就绪队列中优先级最高的任务,⑸处如果当前运行任务和就绪队列汇总优先级最高的任务不是同一个任务,把当前任务状态设置为非运行状态,新任务设置为运行状态,并设置新任务的开始时间为当前任务的开始时间,然后执行⑹标记是否需要任务切换。⑺处把新任务从就绪队列中出队,⑻处计算新任务的运行结束时间,然后执行⑼设置任务到期时间。

BOOL OsSchedTaskSwitch(VOID)
{
    UINT64 endTime;
    BOOL isTaskSwitch = FALSE;
 ⑴ LosTaskCB *runTask = g_losTask.runTask;
    OsTimeSliceUpdate(runTask, OsGetCurrSchedTimeCycle());

⑵  if (runTask->taskStatus & (OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY)) {
        OsAdd2SortLink(&runTask->sortList, runTask->startTime, runTask->waitTimes, OS_SORT_LINK_TASK);
    } else if (!(runTask->taskStatus & (OS_TASK_STATUS_PEND | OS_TASK_STATUS_SUSPEND | OS_TASK_STATUS_UNUSED))) {
⑶      OsSchedTaskEnQueue(runTask);
    }

⑷  LosTaskCB *newTask = OsGetTopTask();
    g_losTask.newTask = newTask;

    if (runTask != newTask) {
#if (LOSCFG_BASE_CORE_TSK_MONITOR == 1)
        OsTaskSwitchCheck();
#endif
⑸      runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING;
        newTask->taskStatus |= OS_TASK_STATUS_RUNNING;
        newTask->startTime = runTask->startTime;
⑹      isTaskSwitch = TRUE;

        OsHookCall(LOS_HOOK_TYPE_TASK_SWITCHEDIN);
    }

⑺  OsSchedTaskDeQueue(newTask);

⑻  if (newTask->taskID != g_idleTaskID) {
        endTime = newTask->startTime + newTask->timeSlice;
    } else {
        endTime = OS_SCHED_MAX_RESPONSE_TIME;
    }
⑼   OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, endTime);

    return isTaskSwitch;
}