You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1225 lines
55 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_sched_pri.h"
#include "los_hw_pri.h"
#include "los_task_pri.h"
#include "los_process_pri.h"
#include "los_arch_mmu.h"
#include "los_hook.h"
#ifdef LOSCFG_KERNEL_CPUP
#include "los_cpup_pri.h"
#endif
#include "los_hw_tick_pri.h"
#include "los_tick_pri.h"
#ifdef LOSCFG_BASE_CORE_TSK_MONITOR
#include "los_stackinfo_pri.h"
#endif
#include "los_mp.h"
#ifdef LOSCFG_SCHED_DEBUG
#include "los_stat_pri.h"
#endif
#define OS_32BIT_MAX 0xFFFFFFFFUL //定义32位操作系统的最大值
#define OS_SCHED_FIFO_TIMEOUT 0x7FFFFFFF //定义FIFO调度器的超时值
#define OS_PRIORITY_QUEUE_NUM 32 //定义优先级队列的数量
#define PRIQUEUE_PRIOR0_BIT 0x80000000U //定义优先级队列中优先级0的最高位
#define OS_SCHED_TIME_SLICES_MIN ((5000 * OS_SYS_NS_PER_US) / OS_NS_PER_CYCLE) /* 定义调度器的最小时间片以纳秒为单位5ms */
#define OS_SCHED_TIME_SLICES_MAX ((LOSCFG_BASE_CORE_TIMESLICE_TIMEOUT * OS_SYS_NS_PER_US) / OS_NS_PER_CYCLE) //定义调度器的最大时间片(以纳秒为单位)
#define OS_SCHED_TIME_SLICES_DIFF (OS_SCHED_TIME_SLICES_MAX - OS_SCHED_TIME_SLICES_MIN) //定义调度器最大时间片和最小时间片之间的差值
#define OS_SCHED_READY_MAX 30 //定义调度器的最大就绪任务数
#define OS_TIME_SLICE_MIN (INT32)((50 * OS_SYS_NS_PER_US) / OS_NS_PER_CYCLE) /*定义最小时间片以INT32类型表示单位为50微秒 50us */
//定义调度器队列结构体SchedQueue
typedef struct {
LOS_DL_LIST priQueueList[OS_PRIORITY_QUEUE_NUM]; // 优先级队列列表
UINT32 readyTasks[OS_PRIORITY_QUEUE_NUM]; // 就绪任务数
UINT32 queueBitmap; // 队列位图
} SchedQueue;
//定义调度器结构体Sched
typedef struct {
SchedQueue queueList[OS_PRIORITY_QUEUE_NUM]; // 优先级队列列表
UINT32 queueBitmap; // 队列位图
SchedScan taskScan; // 任务扫描
SchedScan swtmrScan; // 软件定时器扫描
} Sched;
STATIC Sched *g_sched = NULL; //定义全局调度器变量g_sched
STATIC UINT64 g_schedTickMaxResponseTime; //定义调度器tick的最大响应时间
UINT64 g_sysSchedStartTime = OS_64BIT_MAX; //定义系统调度器的起始时间
#ifdef LOSCFG_SCHED_TICK_DEBUG
#define OS_SCHED_DEBUG_DATA_NUM 1000 //定义调度器tick调试数据数量的常量
//定义调度器tick调试数据结构体SchedTickDebug
typedef struct {
UINT32 tickResporeTime[OS_SCHED_DEBUG_DATA_NUM]; // tick响应时间数组
UINT32 index; // 当前数据索引
UINT32 setTickCount; // 设置的tick计数
UINT64 oldResporeTime; // 上一次的响应时间
} SchedTickDebug;
STATIC SchedTickDebug *g_schedTickDebug = NULL; //定义全局调度器tick调试数据变量g_schedTickDebug
//初始化调度器tick调试数据
STATIC UINT32 OsSchedDebugInit(VOID)
{
UINT32 size = sizeof(SchedTickDebug) * LOSCFG_KERNEL_CORE_NUM;
g_schedTickDebug = (SchedTickDebug *)LOS_MemAlloc(m_aucSysMem0, size);
if (g_schedTickDebug == NULL) {
return LOS_ERRNO_TSK_NO_MEMORY;
}
(VOID)memset_s(g_schedTickDebug, size, 0, size);
return LOS_OK;
}
//记录调度器tick调试数据
VOID OsSchedDebugRecordData(VOID)
{
SchedTickDebug *schedDebug = &g_schedTickDebug[ArchCurrCpuid()];
if (schedDebug->index < OS_SCHED_DEBUG_DATA_NUM) {
UINT64 currTime = OsGetCurrSchedTimeCycle();
schedDebug->tickResporeTime[schedDebug->index] = currTime - schedDebug->oldResporeTime;
schedDebug->oldResporeTime = currTime;
schedDebug->index++;
}
}
//获取调度器tick调试数据
SchedTickDebug *OsSchedDebugGet(VOID)
{
return g_schedTickDebug;
}
// 显示调度器 tick 响应时间的 Shell 命令函数
UINT32 OsShellShowTickRespo(VOID)
{
UINT32 intSave; // 保存中断状态的变量
UINT16 cpu; // CPU 编号
UINT64 allTime; // 所有时间的总和
// 计算存储调度器调试信息的内存大小
UINT32 tickSize = sizeof(SchedTickDebug) * LOSCFG_KERNEL_CORE_NUM;
// 在系统内存池中分配存储调度器调试信息的内存
SchedTickDebug *schedDebug = (SchedTickDebug *)LOS_MemAlloc(m_aucSysMem1, tickSize);
if (schedDebug == NULL) {
return LOS_NOK;
}
// 创建一个数组来保存每个 CPU 上的任务和软件定时器的排序链表节点数
UINT32 sortLinkNum[LOSCFG_KERNEL_CORE_NUM];
// 禁止调度器调度
SCHEDULER_LOCK(intSave);
// 将调度器调试信息拷贝到 schedDebug 中
(VOID)memcpy_s((CHAR *)schedDebug, tickSize, (CHAR *)OsSchedDebugGet(), tickSize);
// 将调度器调试信息清零
(VOID)memset_s((CHAR *)OsSchedDebugGet(), tickSize, 0, tickSize);
// 计算每个 CPU 上的任务和软件定时器的排序链表节点数
for (cpu = 0; cpu < LOSCFG_KERNEL_CORE_NUM; cpu++) {
sortLinkNum[cpu] = OsPercpuGetByID(cpu)->taskSortLink.nodeNum + OsPercpuGetByID(cpu)->swtmrSortLink.nodeNum;
}
// 允许调度器调度
SCHEDULER_UNLOCK(intSave);
// 遍历每个 CPU 上的调度器调试信息
for (cpu = 0; cpu < LOSCFG_KERNEL_CORE_NUM; cpu++) {
SchedTickDebug *schedData = &schedDebug[cpu]; // 获取当前 CPU 的调度器调试信息
// 打印当前 CPU 的调度器调试信息
PRINTK("cpu : %u sched data num : %u set time count : %u SortMax : %u\n",
cpu, schedData->index, schedData->setTickCount, sortLinkNum[cpu]);
UINT32 *data = schedData->tickResporeTime; // 获取当前 CPU 的 tick 响应时间数据
allTime = 0; // 重置总时间
// 遍历 tick 响应时间数据
for (UINT32 i = 1; i < schedData->index; i++) {
allTime += data[i]; // 计算总时间
UINT32 timeUs = (data[i] * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将时间转换为微秒
PRINTK(" %u(%u)", timeUs, timeUs / OS_US_PER_TICK); // 打印时间
if ((i != 0) && ((i % 5) == 0)) { /* A row of 5 data每行显示5个数据 */
PRINTK("\n"); // 换行
}
}
allTime = (allTime * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将总时间转换为微秒
PRINTK("\nTick Indicates the average response period: %llu(us)\n", allTime / (schedData->index - 1)); //平均响应周期
}
// 释放内存
(VOID)LOS_MemFree(m_aucSysMem1, schedDebug);
return LOS_OK;
}
#else
//显示调度器tick响应时间的Shell命令函数
UINT32 OsShellShowTickRespo(VOID)
{
return LOS_NOK;
}
#endif
//定义调度器调试开关
#ifdef LOSCFG_SCHED_DEBUG
// 显示调度器调度参数的 Shell 命令函数
UINT32 OsShellShowSchedParam(VOID)
{
UINT64 averRunTime; // 平均运行时间
UINT64 averTimeSlice; // 平均时间片
UINT64 averSchedWait; // 平均等待调度时间
UINT64 averPendTime; // 平均挂起时间
UINT32 intSave; // 保存中断状态的变量
UINT32 size = g_taskMaxNum * sizeof(LosTaskCB); // 计算任务控制块数组的大小
LosTaskCB *taskCBArray = LOS_MemAlloc(m_aucSysMem1, size); // 在系统内存池中分配任务控制块数组的内存
if (taskCBArray == NULL) {
return LOS_NOK;
}
SCHEDULER_LOCK(intSave); // 禁止调度器调度
// 将全局的任务控制块数组拷贝到局部数组中
(VOID)memcpy_s(taskCBArray, size, g_taskCBArray, size);
SCHEDULER_UNLOCK(intSave); // 允许调度器调度
// 打印表头
PRINTK(" Tid AverRunTime(us) SwitchCount AverTimeSlice(us) TimeSliceCount AverReadyWait(us) "
"AverPendTime(us) TaskName \n");
// 遍历任务控制块数组
for (UINT32 tid = 0; tid < g_taskMaxNum; tid++) {
LosTaskCB *taskCB = taskCBArray + tid; // 获取当前任务控制块
// 如果任务控制块未使用,则跳过
if (OsTaskIsUnused(taskCB)) {
continue;
}
averRunTime = 0; // 平均运行时间初始化为0
averTimeSlice = 0; // 平均时间片初始化为0
averPendTime = 0; // 平均挂起时间初始化为0
averSchedWait = 0; // 平均等待调度时间初始化为0
// 计算平均运行时间
if (taskCB->schedStat.switchCount >= 1) {
averRunTime = taskCB->schedStat.runTime / taskCB->schedStat.switchCount; // 平均运行时间 = 运行时间 / 切换次数
averRunTime = (averRunTime * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将平均运行时间转换为微秒
}
// 计算平均时间片
if (taskCB->schedStat.timeSliceCount > 1) {
averTimeSlice = taskCB->schedStat.timeSliceTime / (taskCB->schedStat.timeSliceCount - 1); // 平均时间片 = 时间片总数 / (时间片个数 - 1)
averTimeSlice = (averTimeSlice * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将平均时间片转换为微秒
}
// 计算平均挂起时间
if (taskCB->schedStat.pendCount > 1) {
averPendTime = taskCB->schedStat.pendTime / taskCB->schedStat.pendCount; // 平均挂起时间 = 挂起时间 / 挂起次数
averPendTime = (averPendTime * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将平均挂起时间转换为微秒
}
// 计算平均等待调度时间
if (taskCB->schedStat.waitSchedCount > 0) {
averSchedWait = taskCB->schedStat.waitSchedTime / taskCB->schedStat.waitSchedCount; // 平均等待调度时间 = 等待调度时间 / 等待调度次数
averSchedWait = (averSchedWait * OS_NS_PER_CYCLE) / OS_SYS_NS_PER_US; // 将平均等待调度时间转换为微秒
}
// 打印任务的调度参数信息
PRINTK("%5u%19llu%15llu%19llu%18llu%19llu%18llu %-32s\n", taskCB->taskID,
averRunTime, taskCB->schedStat.switchCount,
averTimeSlice, taskCB->schedStat.timeSliceCount - 1,
averSchedWait, averPendTime, taskCB->taskName);
}
(VOID)LOS_MemFree(m_aucSysMem1, taskCBArray); // 释放内存
return LOS_OK;
}
#else
UINT32 OsShellShowSchedParam(VOID)
{
return LOS_NOK; // 返回错误码,表示未实现该函数
}
#endif
// 设置调度器的 tick 定时器类型
UINT32 OsSchedSetTickTimerType(UINT32 timerType)
{
switch (timerType) {
case 32: /* 32 位定时器 */
g_schedTickMaxResponseTime = OS_32BIT_MAX; // 设置最大的 tick 响应时间为 32 位定时器的最大值
break;
case 64: /* 64 位定时器 */
g_schedTickMaxResponseTime = OS_64BIT_MAX; // 设置最大的 tick 响应时间为 64 位定时器的最大值
break;
default:
PRINT_ERR("Unsupported Tick Timer type, The system only supports 32 and 64 bit tick timers\n");
return LOS_NOK; // 返回错误码,表示不支持该类型的定时器
}
return LOS_OK; // 返回成功码
}
// 设置调度器的启动时间
STATIC VOID OsSchedSetStartTime(UINT64 currCycle)
{
if (g_sysSchedStartTime == OS_64BIT_MAX) {
g_sysSchedStartTime = currCycle; // 如果系统的调度启动时间未设置,则设置为当前的时钟周期数
}
}
// 更新时间片
STATIC INLINE VOID OsTimeSliceUpdate(LosTaskCB *taskCB, UINT64 currTime)
{
LOS_ASSERT(currTime >= taskCB->startTime); // 断言当前时间大于等于任务的启动时间
INT32 incTime = (currTime - taskCB->startTime - taskCB->irqUsedTime); // 计算增加的时间
LOS_ASSERT(incTime >= 0); // 断言增加的时间大于等于0
if (taskCB->policy == LOS_SCHED_RR) {
taskCB->timeSlice -= incTime; // 更新时间片剩余时间
#ifdef LOSCFG_SCHED_DEBUG
taskCB->schedStat.timeSliceRealTime += incTime; // 更新调度统计信息中的实际时间片使用时间
#endif
}
taskCB->irqUsedTime = 0; // 清零中断使用的时间
taskCB->startTime = currTime; // 更新任务的启动时间
#ifdef LOSCFG_SCHED_DEBUG
taskCB->schedStat.allRuntime += incTime; // 更新调度统计信息中的总运行时间
#endif
}
// 重新加载 tick 定时器
STATIC INLINE VOID OsSchedTickReload(Percpu *currCpu, UINT64 nextResponseTime, UINT32 responseID, BOOL isTimeSlice)
{
UINT64 currTime, nextExpireTime;
UINT32 usedTime;
currTime = OsGetCurrSchedTimeCycle(); // 获取当前的调度时间
if (currCpu->tickStartTime != 0) {
usedTime = currTime - currCpu->tickStartTime; // 计算 tick 定时器已经使用的时间
currCpu->tickStartTime = 0; // 清零 tick 定时器的启动时间
} else {
usedTime = 0;
}
if ((nextResponseTime > usedTime) && ((nextResponseTime - usedTime) > OS_TICK_RESPONSE_PRECISION)) {
nextResponseTime -= usedTime; // 减去已经使用的时间,得到下一次 tick 定时器的响应时间
} else {
nextResponseTime = OS_TICK_RESPONSE_PRECISION; // 如果计算出的响应时间小于精度要求,设置为精度要求
}
nextExpireTime = currTime + nextResponseTime; // 计算下一次 tick 定时器的到期时间
if (nextExpireTime >= currCpu->responseTime) {
return; // 如果下一次到期时间大于等于当前的响应时间,直接返回
}
if (isTimeSlice) {
/* 当前系统的到期时间是线程的时间片到期时间 */
currCpu->responseID = responseID; // 设置当前 CPU 的响应 ID
} else {
currCpu->responseID = OS_INVALID_VALUE; // 设置当前 CPU 的响应 ID 为无效值
}
currCpu->responseTime = nextExpireTime; // 更新当前 CPU 的响应时间
HalClockTickTimerReload(nextResponseTime); // 重新加载 tick 定时器
#ifdef LOSCFG_SCHED_TICK_DEBUG
SchedTickDebug *schedDebug = &g_schedTickDebug[ArchCurrCpuid()];
if (schedDebug->index < OS_SCHED_DEBUG_DATA_NUM) {
schedDebug->setTickCount++; // 更新调度 tick 调试信息中的设置 tick 数量
}
#endif
}
STATIC INLINE VOID OsSchedSetNextExpireTime(UINT64 startTime, UINT32 responseID,
UINT64 taskEndTime, UINT32 oldResponseID)
{
UINT64 nextExpireTime = OsGetNextExpireTime(startTime); // 获取下一次到期时间
Percpu *currCpu = OsPercpuGet(); // 获取当前 CPU 的数据结构指针
UINT64 nextResponseTime;
BOOL isTimeSlice = FALSE;
currCpu->schedFlag &= ~INT_PEND_TICK; // 清除调度标志位中的中断挂起标志
if (currCpu->responseID == oldResponseID) {
/* 此次已经到期,下一次理论上到期的时间为无穷大 */
currCpu->responseTime = OS_SCHED_MAX_RESPONSE_TIME;
}
/* 当前线程的时间片已经消耗完,但当前系统锁定任务无法触发调度以释放 CPU */
if ((nextExpireTime > taskEndTime) && ((nextExpireTime - taskEndTime) > OS_SCHED_MINI_PERIOD)) {
nextExpireTime = taskEndTime;
isTimeSlice = TRUE; // 设置时间片到期标志
}
if ((currCpu->responseTime > nextExpireTime) &&
((currCpu->responseTime - nextExpireTime) >= OS_TICK_RESPONSE_PRECISION)) {
nextResponseTime = nextExpireTime - startTime; // 计算下一次响应时间
if (nextResponseTime > g_schedTickMaxResponseTime) {
nextResponseTime = g_schedTickMaxResponseTime; // 如果下一次响应时间超过最大响应时间,设置为最大响应时间
}
} else {
/* 没有比当前到期时间更早的点 */
currCpu->tickStartTime = 0; // 清零 tick 定时器的启动时间
return;
}
OsSchedTickReload(currCpu, nextResponseTime, responseID, isTimeSlice); // 重新加载 tick 定时器
}
VOID OsSchedUpdateExpireTime(UINT64 startTime)
{
UINT64 endTime;
Percpu *cpu = OsPercpuGet(); // 获取当前 CPU 的数据结构指针
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务的控制块指针
if (!OS_SCHEDULER_ACTIVE || OS_INT_ACTIVE) {
cpu->schedFlag |= INT_PEND_TICK; // 如果调度器不活跃或者中断活跃,设置调度标志位中的中断挂起标志
return;
}
if (runTask->policy == LOS_SCHED_RR) {
LOS_SpinLock(&g_taskSpin);
INT32 timeSlice = (runTask->timeSlice <= OS_TIME_SLICE_MIN) ? runTask->initTimeSlice : runTask->timeSlice;
LOS_SpinUnlock(&g_taskSpin);
endTime = startTime + timeSlice; // 计算任务的结束时间
} else {
endTime = OS_SCHED_MAX_RESPONSE_TIME - OS_TICK_RESPONSE_PRECISION; // 如果不是轮转调度策略,设置结束时间为最大响应时间减去精度要求
}
OsSchedSetNextExpireTime(startTime, runTask->taskID, endTime, runTask->taskID); // 设置下一次到期时间
}
STATIC INLINE UINT32 OsSchedCalculateTimeSlice(UINT16 proPriority, UINT16 priority)
{
UINT32 ratTime, readTasks;
SchedQueue *queueList = &g_sched->queueList[proPriority]; // 获取指定优先级的调度队列
readTasks = queueList->readyTasks[priority]; // 获取指定优先级的就绪任务数量
if (readTasks > OS_SCHED_READY_MAX) {
return OS_SCHED_TIME_SLICES_MIN; // 如果就绪任务数量超过最大限制,返回最小时间片
}
ratTime = ((OS_SCHED_READY_MAX - readTasks) * OS_SCHED_TIME_SLICES_DIFF) / OS_SCHED_READY_MAX; // 计算时间片
return (ratTime + OS_SCHED_TIME_SLICES_MIN); // 返回计算出的时间片
}
STATIC INLINE VOID OsSchedPriQueueEnHead(UINT32 proPriority, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
SchedQueue *queueList = &g_sched->queueList[proPriority]; // 获取指定优先级的调度队列
LOS_DL_LIST *priQueueList = &queueList->priQueueList[0]; // 获取指定优先级的优先级队列
UINT32 *bitMap = &queueList->queueBitmap; // 获取位图
/*
* 任务控制块被初始化为零。当任务被删除时,
* 同时从优先级队列或其他列表中删除,任务挂起节点将被恢复为零。
*/
LOS_ASSERT(priqueueItem->pstNext == NULL); // 断言优先级队列项的下一个节点为空
if (*bitMap == 0) {
g_sched->queueBitmap |= PRIQUEUE_PRIOR0_BIT >> proPriority; // 如果位图为零,设置全局位图中对应的位
}
if (LOS_ListEmpty(&priQueueList[priority])) {
*bitMap |= PRIQUEUE_PRIOR0_BIT >> priority; // 如果指定优先级的优先级队列为空,设置位图中对应的位
}
LOS_ListHeadInsert(&priQueueList[priority], priqueueItem); // 将优先级队列项插入到指定优先级的队列头部
queueList->readyTasks[priority]++; // 增加指定优先级的就绪任务数量
}
STATIC INLINE VOID OsSchedPriQueueEnTail(UINT32 proPriority, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
SchedQueue *queueList = &g_sched->queueList[proPriority]; // 获取指定优先级的调度队列
LOS_DL_LIST *priQueueList = &queueList->priQueueList[0]; // 获取指定优先级的优先级队列
UINT32 *bitMap = &queueList->queueBitmap; // 获取位图
/*
* 任务控制块被初始化为零。当任务被删除时,
* 同时从优先级队列或其他列表中删除,任务挂起节点将被恢复为零。
*/
LOS_ASSERT(priqueueItem->pstNext == NULL); // 断言优先级队列项的下一个节点为空
if (*bitMap == 0) {
g_sched->queueBitmap |= PRIQUEUE_PRIOR0_BIT >> proPriority; // 如果位图为零,设置全局位图中对应的位
}
if (LOS_ListEmpty(&priQueueList[priority])) {
*bitMap |= PRIQUEUE_PRIOR0_BIT >> priority; // 如果指定优先级的优先级队列为空,设置位图中对应的位
}
LOS_ListTailInsert(&priQueueList[priority], priqueueItem); // 将优先级队列项插入到指定优先级的队列尾部
queueList->readyTasks[priority]++; // 增加指定优先级的就绪任务数量
}
STATIC INLINE VOID OsSchedPriQueueDelete(UINT32 proPriority, LOS_DL_LIST *priqueueItem, UINT32 priority)
{
SchedQueue *queueList = &g_sched->queueList[proPriority]; // 获取指定优先级的调度队列
LOS_DL_LIST *priQueueList = &queueList->priQueueList[0]; // 获取指定优先级的优先级队列
UINT32 *bitMap = &queueList->queueBitmap; // 获取位图
LOS_ListDelete(priqueueItem); // 从优先级队列中删除优先级队列项
queueList->readyTasks[priority]--; // 减少指定优先级的就绪任务数量
if (LOS_ListEmpty(&priQueueList[priority])) {
*bitMap &= ~(PRIQUEUE_PRIOR0_BIT >> priority); // 如果指定优先级的优先级队列为空,清除位图中对应的位
}
if (*bitMap == 0) {
g_sched->queueBitmap &= ~(PRIQUEUE_PRIOR0_BIT >> proPriority); // 如果位图为零,清除全局位图中对应的位
}
}
STATIC INLINE VOID OsSchedWakePendTimeTask(UINT64 currTime, LosTaskCB *taskCB, BOOL *needSchedule)
{
#ifndef LOSCFG_SCHED_DEBUG
(VOID)currTime;
#endif
LOS_SpinLock(&g_taskSpin); // 获取全局任务自旋锁
UINT16 tempStatus = taskCB->taskStatus; // 获取任务的状态
if (tempStatus & (OS_TASK_STATUS_PENDING | OS_TASK_STATUS_DELAY)) {
taskCB->taskStatus &= ~(OS_TASK_STATUS_PENDING | OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY); // 清除任务的挂起状态和延时状态
if (tempStatus & OS_TASK_STATUS_PENDING) {
#ifdef LOSCFG_KERNEL_LITEIPC
taskCB->ipcStatus &= ~IPC_THREAD_STATUS_PEND; // 清除任务的轻量级 IPC 挂起状态
#endif
taskCB->taskStatus |= OS_TASK_STATUS_TIMEOUT; // 设置任务的超时状态
LOS_ListDelete(&taskCB->pendList); // 从挂起任务列表中删除任务
taskCB->taskMux = NULL; // 清除任务的互斥信号量
OsTaskWakeClearPendMask(taskCB); // 清除任务的挂起掩码
}
if (!(tempStatus & OS_TASK_STATUS_SUSPENDED)) {
#ifdef LOSCFG_SCHED_DEBUG
taskCB->schedStat.pendTime += currTime - taskCB->startTime; // 更新任务的挂起时间统计
taskCB->schedStat.pendCount++; // 增加任务的挂起次数统计
#endif
OsSchedTaskEnQueue(taskCB); // 将任务加入调度队列
*needSchedule = TRUE; // 设置需要进行调度
}
}
LOS_SpinUnlock(&g_taskSpin); // 释放全局任务自旋锁
}
STATIC INLINE BOOL OsSchedScanTimerList(VOID)
{
Percpu *cpu = OsPercpuGet(); // 获取当前 CPU 的 Per-CPU 变量
BOOL needSchedule = FALSE; // 标记是否需要进行调度
SortLinkAttribute *taskSortLink = &OsPercpuGet()->taskSortLink; // 获取当前 CPU 的任务排序链表属性
LOS_DL_LIST *listObject = &taskSortLink->sortLink; // 获取当前 CPU 的任务排序链表
/*
* 当任务被挂起并设置了超时时,任务块会同时存在于超时排序链表
* (每个 CPU 都有一个) 和相应的 IPC互斥信号量、信号量等的链表中
* 它可以通过超时或等待的 IPC 被唤醒。
*
* 现在使用同步的排序链表过程,因此整个任务扫描需要保护,
* 防止另一个核心同时进行排序链表的删除操作。
*/
LOS_SpinLock(&cpu->taskSortLinkSpin); // 获取当前 CPU 的任务排序链表自旋锁
if (LOS_ListEmpty(listObject)) {
LOS_SpinUnlock(&cpu->taskSortLinkSpin);
return needSchedule;
}
SortLinkList *sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); // 获取排序链表的第一个任务节点
UINT64 currTime = OsGetCurrSchedTimeCycle(); // 获取当前调度时间
while (sortList->responseTime <= currTime) {
LosTaskCB *taskCB = LOS_DL_LIST_ENTRY(sortList, LosTaskCB, sortList); // 获取任务控制块
OsDeleteNodeSortLink(taskSortLink, &taskCB->sortList); // 从排序链表中删除任务节点
LOS_SpinUnlock(&cpu->taskSortLinkSpin); // 释放当前 CPU 的任务排序链表自旋锁
OsSchedWakePendTimeTask(currTime, taskCB, &needSchedule); // 唤醒被挂起的超时任务
LOS_SpinLock(&cpu->taskSortLinkSpin); // 获取当前 CPU 的任务排序链表自旋锁
if (LOS_ListEmpty(listObject)) {
break;
}
sortList = LOS_DL_LIST_ENTRY(listObject->pstNext, SortLinkList, sortLinkNode); // 获取排序链表的下一个任务节点
}
LOS_SpinUnlock(&cpu->taskSortLinkSpin); // 释放当前 CPU 的任务排序链表自旋锁
return needSchedule; // 返回是否需要进行调度
}
STATIC INLINE VOID OsSchedEnTaskQueue(LosTaskCB *taskCB, LosProcessCB *processCB)
{
LOS_ASSERT(!(taskCB->taskStatus & OS_TASK_STATUS_READY)); // 断言任务不处于就绪状态
switch (taskCB->policy) {
case LOS_SCHED_RR: {
if (taskCB->timeSlice > OS_TIME_SLICE_MIN) { // 如果时间片大于最小时间片
OsSchedPriQueueEnHead(processCB->priority, &taskCB->pendList, taskCB->priority); // 将任务加入就绪队列的头部
} else {
taskCB->initTimeSlice = OsSchedCalculateTimeSlice(processCB->priority, taskCB->priority); // 计算任务的初始时间片
taskCB->timeSlice = taskCB->initTimeSlice; // 设置任务的时间片
OsSchedPriQueueEnTail(processCB->priority, &taskCB->pendList, taskCB->priority); // 将任务加入就绪队列的尾部
#ifdef LOSCFG_SCHED_DEBUG
taskCB->schedStat.timeSliceTime = taskCB->schedStat.timeSliceRealTime; // 更新任务的时间片统计
taskCB->schedStat.timeSliceCount++; // 增加任务的时间片计数
#endif
}
break;
}
case LOS_SCHED_FIFO: {
/* FIFO 调度策略的时间片总是大于 0除非调用了 yield */
if ((taskCB->timeSlice > OS_TIME_SLICE_MIN) && (taskCB->taskStatus & OS_TASK_STATUS_RUNNING)) { // 如果时间片大于最小时间片且任务正在运行
OsSchedPriQueueEnHead(processCB->priority, &taskCB->pendList, taskCB->priority); // 将任务加入就绪队列的头部
} else {
taskCB->initTimeSlice = OS_SCHED_FIFO_TIMEOUT; // 设置任务的初始时间片为 FIFO 超时时间
taskCB->timeSlice = taskCB->initTimeSlice; // 设置任务的时间片
OsSchedPriQueueEnTail(processCB->priority, &taskCB->pendList, taskCB->priority); // 将任务加入就绪队列的尾部
}
break;
}
case LOS_SCHED_IDLE:
#ifdef LOSCFG_SCHED_DEBUG
taskCB->schedStat.timeSliceCount = 1; // 设置任务的时间片计数为 1
#endif
break;
default:
LOS_ASSERT(0); // 断言不会执行到这里
break;
}
taskCB->taskStatus &= ~OS_TASK_STATUS_BLOCKED; // 清除任务的阻塞状态
taskCB->taskStatus |= OS_TASK_STATUS_READY; // 设置任务的就绪状态
processCB->processStatus &= ~(OS_PROCESS_STATUS_INIT | OS_PROCESS_STATUS_PENDING); // 清除进程的初始化和挂起状态
processCB->processStatus |= OS_PROCESS_STATUS_READY; // 设置进程的就绪状态
processCB->readyTaskNum++; // 增加进程的就绪任务数
}
STATIC INLINE VOID OsSchedDeTaskQueue(LosTaskCB *taskCB, LosProcessCB *processCB)
{
if (taskCB->policy != LOS_SCHED_IDLE) { // 如果任务的调度策略不是 IDLE
OsSchedPriQueueDelete(processCB->priority, &taskCB->pendList, taskCB->priority); // 从就绪队列中删除任务
}
taskCB->taskStatus &= ~OS_TASK_STATUS_READY; // 清除任务的就绪状态
processCB->readyTaskNum--; // 减少进程的就绪任务数
if (processCB->readyTaskNum == 0) {
processCB->processStatus &= ~OS_PROCESS_STATUS_READY; // 如果进程的就绪任务数为 0则清除进程的就绪状态
}
}
VOID OsSchedTaskDeQueue(LosTaskCB *taskCB)
{
LosProcessCB *processCB = OS_PCB_FROM_PID(taskCB->processID); // 获取任务所属进程的控制块
if (taskCB->taskStatus & OS_TASK_STATUS_READY) { // 如果任务处于就绪状态
OsSchedDeTaskQueue(taskCB, processCB); // 从就绪队列中删除任务
}
if (processCB->processStatus & OS_PROCESS_STATUS_READY) { // 如果进程处于就绪状态
return;
}
/* 如果当前进程只有当前线程在运行,
* 那么线程离开调度队列后进程将变为阻塞状态
*/
if (OS_PROCESS_GET_RUNTASK_COUNT(processCB->processStatus) == 1) {
processCB->processStatus |= OS_PROCESS_STATUS_PENDING; // 设置进程的挂起状态
}
}
VOID OsSchedTaskEnQueue(LosTaskCB *taskCB)
{
LosProcessCB *processCB = OS_PCB_FROM_PID(taskCB->processID); // 获取任务所属进程的控制块
#ifdef LOSCFG_SCHED_DEBUG
if (!(taskCB->taskStatus & OS_TASK_STATUS_RUNNING)) { // 如果任务不是正在运行状态
taskCB->startTime = OsGetCurrSchedTimeCycle(); // 记录任务的启动时间
}
#endif
OsSchedEnTaskQueue(taskCB, processCB); // 将任务加入就绪队列
}
VOID OsSchedTaskExit(LosTaskCB *taskCB)
{
LosProcessCB *processCB = OS_PCB_FROM_PID(taskCB->processID); // 获取任务所属进程的控制块
if (taskCB->taskStatus & OS_TASK_STATUS_READY) { // 如果任务处于就绪状态
OsSchedTaskDeQueue(taskCB); // 从就绪队列中删除任务
processCB->processStatus &= ~OS_PROCESS_STATUS_PENDING; // 清除进程的挂起状态
} else if (taskCB->taskStatus & OS_TASK_STATUS_PENDING) { // 如果任务处于挂起状态
LOS_ListDelete(&taskCB->pendList); // 从挂起队列中删除任务
taskCB->taskStatus &= ~OS_TASK_STATUS_PENDING; // 清除任务的挂起状态
}
if (taskCB->taskStatus & (OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND_TIME)) { // 如果任务处于延时或等待定时状态
OsDeleteSortLink(&taskCB->sortList, OS_SORT_LINK_TASK); // 从排序链表中删除任务
taskCB->taskStatus &= ~(OS_TASK_STATUS_DELAY | OS_TASK_STATUS_PEND_TIME); // 清除任务的延时和等待定时状态
}
}
VOID OsSchedYield(VOID)
{
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
runTask->timeSlice = 0; // 将任务的时间片设置为 0
runTask->startTime = OsGetCurrSchedTimeCycle(); // 记录任务的启动时间
OsSchedTaskEnQueue(runTask); // 将任务加入就绪队列
OsSchedResched(); // 进行调度
}
VOID OsSchedDelay(LosTaskCB *runTask, UINT32 tick)
{
OsSchedTaskDeQueue(runTask); // 从就绪队列中删除任务
runTask->taskStatus |= OS_TASK_STATUS_DELAY; // 设置任务的延时状态
runTask->waitTimes = tick; // 设置任务的等待时间
OsSchedResched(); // 进行调度
}
UINT32 OsSchedTaskWait(LOS_DL_LIST *list, UINT32 ticks, BOOL needSched)
{
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
OsSchedTaskDeQueue(runTask); // 从就绪队列中删除任务
runTask->taskStatus |= OS_TASK_STATUS_PENDING; // 设置任务的挂起状态
LOS_ListTailInsert(list, &runTask->pendList); // 将任务插入挂起队列的尾部
if (ticks != LOS_WAIT_FOREVER) { // 如果等待时间不是永远
runTask->taskStatus |= OS_TASK_STATUS_PEND_TIME; // 设置任务的等待定时状态
runTask->waitTimes = ticks; // 设置任务的等待时间
}
if (needSched == TRUE) { // 如果需要进行调度
OsSchedResched(); // 进行调度
if (runTask->taskStatus & OS_TASK_STATUS_TIMEOUT) { // 如果任务超时
runTask->taskStatus &= ~OS_TASK_STATUS_TIMEOUT; // 清除任务的超时状态
return LOS_ERRNO_TSK_TIMEOUT; // 返回超时错误
}
}
return LOS_OK; // 返回成功
}
VOID OsSchedTaskWake(LosTaskCB *resumedTask)
{
LOS_ListDelete(&resumedTask->pendList); // 从挂起队列中删除任务
resumedTask->taskStatus &= ~OS_TASK_STATUS_PENDING; // 清除任务的挂起状态
if (resumedTask->taskStatus & OS_TASK_STATUS_PEND_TIME) { // 如果任务处于等待定时状态
OsDeleteSortLink(&resumedTask->sortList, OS_SORT_LINK_TASK); // 从排序链表中删除任务
resumedTask->taskStatus &= ~OS_TASK_STATUS_PEND_TIME; // 清除任务的等待定时状态
}
if (!(resumedTask->taskStatus & OS_TASK_STATUS_SUSPENDED)) { // 如果任务没有被挂起
#ifdef LOSCFG_SCHED_DEBUG
resumedTask->schedStat.pendTime += OsGetCurrSchedTimeCycle() - resumedTask->startTime;
resumedTask->schedStat.pendCount++;
#endif
OsSchedTaskEnQueue(resumedTask);
}
}
BOOL OsSchedModifyTaskSchedParam(LosTaskCB *taskCB, UINT16 policy, UINT16 priority)
{
if (taskCB->policy != policy) { // 如果任务的调度策略与传入的策略不同
taskCB->policy = policy; // 更新任务的调度策略
taskCB->timeSlice = 0; // 将任务的时间片设置为 0
}
if (taskCB->taskStatus & OS_TASK_STATUS_READY) { // 如果任务处于就绪状态
OsSchedTaskDeQueue(taskCB); // 从就绪队列中删除任务
taskCB->priority = priority; // 更新任务的优先级
OsSchedTaskEnQueue(taskCB); // 将任务加入就绪队列
return TRUE; // 返回需要进行调度
}
taskCB->priority = priority; // 更新任务的优先级
OsHookCall(LOS_HOOK_TYPE_TASK_PRIMODIFY, taskCB, taskCB->priority); // 调用任务优先级修改的钩子函数
if (taskCB->taskStatus & OS_TASK_STATUS_INIT) { // 如果任务处于初始化状态
OsSchedTaskEnQueue(taskCB); // 将任务加入就绪队列
return TRUE; // 返回需要进行调度
}
if (taskCB->taskStatus & OS_TASK_STATUS_RUNNING) { // 如果任务处于运行状态
return TRUE; // 返回需要进行调度
}
return FALSE; // 返回不需要进行调度
}
BOOL OsSchedModifyProcessSchedParam(LosProcessCB *processCB, UINT16 policy, UINT16 priority)
{
LosTaskCB *taskCB = NULL;
BOOL needSched = FALSE;
(VOID)policy; // 忽略传入的调度策略
if (processCB->processStatus & OS_PROCESS_STATUS_READY) { // 如果进程处于就绪状态
LOS_DL_LIST_FOR_EACH_ENTRY(taskCB, &processCB->threadSiblingList, LosTaskCB, threadList) {
if (taskCB->taskStatus & OS_TASK_STATUS_READY) { // 如果任务处于就绪状态
OsSchedPriQueueDelete(processCB->priority, &taskCB->pendList, taskCB->priority); // 从优先级队列中删除任务
OsSchedPriQueueEnTail(priority, &taskCB->pendList, taskCB->priority); // 将任务按照新的优先级插入到优先级队列中
needSched = TRUE; // 需要进行调度
}
}
}
processCB->priority = priority; // 更新进程的优先级
if (processCB->processStatus & OS_PROCESS_STATUS_RUNNING) { // 如果进程处于运行状态
needSched = TRUE; // 需要进行调度
}
return needSched; // 返回是否需要进行调度
}
VOID OsSchedTick(VOID)
{
Sched *sched = g_sched; // 获取全局调度器结构体指针
Percpu *currCpu = OsPercpuGet(); // 获取当前 CPU 的私有数据结构指针
BOOL needSched = FALSE; // 是否需要进行调度
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
currCpu->tickStartTime = runTask->irqStartTime; // 记录当前时钟中断的开始时间
if (currCpu->responseID == OS_INVALID_VALUE) { // 如果当前 CPU 的响应 ID 无效
if (sched->swtmrScan != NULL) { // 如果软件定时器扫描函数不为空
(VOID)sched->swtmrScan(); // 执行软件定时器扫描函数
}
needSched = sched->taskScan(); // 执行任务扫描函数,判断是否需要进行调度
if (needSched) { // 如果需要进行调度
LOS_MpSchedule(OS_MP_CPU_ALL); // 进行多核调度
currCpu->schedFlag |= INT_PEND_RESCH; // 设置调度标志位
}
}
currCpu->schedFlag |= INT_PEND_TICK; // 设置时钟中断调度标志位
currCpu->responseTime = OS_SCHED_MAX_RESPONSE_TIME; // 设置响应时间的上限
}
VOID OsSchedSetIdleTaskSchedParam(LosTaskCB *idleTask)
{
idleTask->policy = LOS_SCHED_IDLE; // 设置空闲任务的调度策略为 LOS_SCHED_IDLE
idleTask->initTimeSlice = OS_SCHED_FIFO_TIMEOUT; // 设置空闲任务的初始时间片
idleTask->timeSlice = idleTask->initTimeSlice; // 设置空闲任务的时间片
OsSchedTaskEnQueue(idleTask); // 将空闲任务加入就绪队列
}
UINT32 OsSchedSwtmrScanRegister(SchedScan func)
{
if (func == NULL) { // 如果传入的函数指针为空
return LOS_NOK; // 返回失败
}
g_sched->swtmrScan = func; // 注册软件定时器扫描函数
return LOS_OK; // 返回成功
}
UINT32 OsSchedInit(VOID)
{
UINT16 index, pri;
UINT32 ret;
g_sched = (Sched *)LOS_MemAlloc(m_aucSysMem0, sizeof(Sched)); // 分配内存给全局调度器结构体指针
if (g_sched == NULL) { // 如果内存分配失败
return LOS_ERRNO_TSK_NO_MEMORY; // 返回内存不足错误
}
(VOID)memset_s(g_sched, sizeof(Sched), 0, sizeof(Sched)); // 将全局调度器结构体清零
for (index = 0; index < OS_PRIORITY_QUEUE_NUM; index++) { // 遍历优先级队列
SchedQueue *queueList = &g_sched->queueList[index];
LOS_DL_LIST *priList = &queueList->priQueueList[0];
for (pri = 0; pri < OS_PRIORITY_QUEUE_NUM; pri++) { // 遍历每个优先级队列的优先级
LOS_ListInit(&priList[pri]); // 初始化优先级队列
}
}
for (index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) { // 遍历每个 CPU 的私有数据结构
Percpu *cpu = OsPercpuGetByID(index); // 获取当前 CPU 的私有数据结构指针
ret = OsSortLinkInit(&cpu->taskSortLink); // 初始化任务排序链表
if (ret != LOS_OK) { // 如果初始化失败
return LOS_ERRNO_TSK_NO_MEMORY; // 返回内存不足错误
}
cpu->responseTime = OS_SCHED_MAX_RESPONSE_TIME; // 设置响应时间的上限
LOS_SpinInit(&cpu->taskSortLinkSpin); // 初始化任务排序链表的自旋锁
LOS_SpinInit(&cpu->swtmrSortLinkSpin); // 初始化软件定时器排序链表的自旋锁
}
g_sched->taskScan = OsSchedScanTimerList; // 设置任务扫描函数为默认的定时器扫描函数
#ifdef LOSCFG_SCHED_TICK_DEBUG
ret = OsSchedDebugInit(); // 初始化调度器的调试功能
if (ret != LOS_OK) { // 如果初始化失败
return ret; // 返回错误码
}
#endif
return LOS_OK; // 返回成功
}
STATIC LosTaskCB *OsGetTopTask(VOID)
{
UINT32 priority, processPriority;
UINT32 bitmap;
LosTaskCB *newTask = NULL;
UINT32 processBitmap = g_sched->queueBitmap; // 获取全局调度器的进程位图
#ifdef LOSCFG_KERNEL_SMP
UINT32 cpuid = ArchCurrCpuid(); // 获取当前 CPU 的 ID
#endif
while (processBitmap) { // 遍历进程位图
processPriority = CLZ(processBitmap); // 获取最高优先级的进程
SchedQueue *queueList = &g_sched->queueList[processPriority]; // 获取对应优先级的队列
bitmap = queueList->queueBitmap; // 获取优先级队列的位图
while (bitmap) { // 遍历优先级队列的位图
priority = CLZ(bitmap); // 获取最高优先级的任务
LOS_DL_LIST_FOR_EACH_ENTRY(newTask, &queueList->priQueueList[priority], LosTaskCB, pendList) {
#ifdef LOSCFG_KERNEL_SMP
if (newTask->cpuAffiMask & (1U << cpuid)) { // 如果任务可以在当前 CPU 上运行
#endif
goto FIND_TASK; // 跳转到找到任务的位置
#ifdef LOSCFG_KERNEL_SMP
}
#endif
}
bitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - priority - 1)); // 清除已经遍历过的位
}
processBitmap &= ~(1U << (OS_PRIORITY_QUEUE_NUM - processPriority - 1)); // 清除已经遍历过的位
}
newTask = OS_TCB_FROM_TID(OsPercpuGet()->idleTaskID); // 如果没有找到任务,则返回空闲任务
FIND_TASK:
OsSchedDeTaskQueue(newTask, OS_PCB_FROM_PID(newTask->processID)); // 从任务队列中删除任务
return newTask; // 返回找到的任务
}
VOID OsSchedStart(VOID)
{
UINT32 cpuid = ArchCurrCpuid(); // 获取当前 CPU 的 ID
UINT32 intSave;
SCHEDULER_LOCK(intSave); // 锁住调度器
if (cpuid == 0) { // 如果是第一个 CPU
OsTickStart(); // 启动系统时钟中断
}
LosTaskCB *newTask = OsGetTopTask(); // 获取要运行的任务
LosProcessCB *newProcess = OS_PCB_FROM_PID(newTask->processID); // 获取任务所属的进程
newTask->taskStatus |= OS_TASK_STATUS_RUNNING; // 设置任务状态为运行中
newProcess->processStatus |= OS_PROCESS_STATUS_RUNNING; // 设置进程状态为运行中
newProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_ADD(newProcess->processStatus); // 增加进程的运行任务计数
OsSchedSetStartTime(HalClockGetCycles()); // 设置调度器的起始时间
newTask->startTime = OsGetCurrSchedTimeCycle(); // 设置任务的起始时间
#ifdef LOSCFG_KERNEL_SMP
/*
* attention: current cpu needs to be set, in case first task deletion
* may fail because this flag mismatch with the real current cpu.
*/
newTask->currCpu = cpuid; // 设置任务所在的 CPU
#endif
OsCurrTaskSet((VOID *)newTask); // 设置当前任务
/* System start schedule */
OS_SCHEDULER_SET(cpuid); // 设置调度器标志,表示系统已经开始调度
OsPercpuGet()->responseID = OS_INVALID; // 初始化响应 ID
OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, newTask->startTime + newTask->timeSlice, OS_INVALID); // 设置下一个任务的到期时间
PRINTK("cpu %d entering scheduler\n", cpuid); // 打印进入调度器的消息
OsTaskContextLoad(newTask); // 加载任务上下文并开始执行任务
}
#ifdef LOSCFG_KERNEL_SMP
VOID OsSchedToUserReleaseLock(VOID)
{
/* The scheduling lock needs to be released before returning to user mode */
LOCKDEP_CHECK_OUT(&g_taskSpin); // 检查任务自旋锁是否正确释放
ArchSpinUnlock(&g_taskSpin.rawLock); // 解锁任务自旋锁
OsPercpuGet()->taskLockCnt--; // 减少任务锁计数
}
#endif
#ifdef LOSCFG_BASE_CORE_TSK_MONITOR
STATIC VOID OsTaskStackCheck(LosTaskCB *runTask, LosTaskCB *newTask)
{
if (!OS_STACK_MAGIC_CHECK(runTask->topOfStack)) { // 检查当前运行任务的栈是否溢出
LOS_Panic("CURRENT task ID: %s:%d stack overflow!\n", runTask->taskName, runTask->taskID); // 如果溢出,触发异常并打印错误信息
}
if (((UINTPTR)(newTask->stackPointer) <= newTask->topOfStack) ||
((UINTPTR)(newTask->stackPointer) > (newTask->topOfStack + newTask->stackSize))) { // 检查新任务的栈指针是否合法
LOS_Panic("HIGHEST task ID: %s:%u SP error! StackPointer: %p TopOfStack: %p\n",
newTask->taskName, newTask->taskID, newTask->stackPointer, newTask->topOfStack); // 如果不合法,触发异常并打印错误信息
}
}
#endif
STATIC INLINE VOID OsSchedSwitchCheck(LosTaskCB *runTask, LosTaskCB *newTask)
{
#ifdef LOSCFG_BASE_CORE_TSK_MONITOR
OsTaskStackCheck(runTask, newTask); // 检查任务栈的合法性
#endif /* LOSCFG_BASE_CORE_TSK_MONITOR */
OsHookCall(LOS_HOOK_TYPE_TASK_SWITCHEDIN, newTask, runTask); // 调用任务切换回调函数
}
STATIC INLINE VOID OsSchedSwitchProcess(LosProcessCB *runProcess, LosProcessCB *newProcess)
{
runProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_DEC(runProcess->processStatus); // 减少当前进程的运行任务计数
newProcess->processStatus = OS_PROCESS_RUNTASK_COUNT_ADD(newProcess->processStatus); // 增加新进程的运行任务计数
LOS_ASSERT(!(OS_PROCESS_GET_RUNTASK_COUNT(newProcess->processStatus) > LOSCFG_KERNEL_CORE_NUM)); // 断言新进程的运行任务计数不超过 CPU 核心数
if (OS_PROCESS_GET_RUNTASK_COUNT(runProcess->processStatus) == 0) { // 如果当前进程的运行任务计数为 0
runProcess->processStatus &= ~OS_PROCESS_STATUS_RUNNING; // 清除当前进程的运行状态标志
}
LOS_ASSERT(!(newProcess->processStatus & OS_PROCESS_STATUS_PENDING)); // 断言新进程的状态不是挂起状态
newProcess->processStatus |= OS_PROCESS_STATUS_RUNNING; // 设置新进程的运行状态标志
#ifdef LOSCFG_KERNEL_VM
if (OsProcessIsUserMode(newProcess)) { // 如果新进程是用户态进程
LOS_ArchMmuContextSwitch(&newProcess->vmSpace->archMmu); // 切换内存管理单元上下文
}
#endif
OsCurrProcessSet(newProcess); // 设置当前进程为新进程
}
STATIC VOID OsSchedTaskSwicth(LosTaskCB *runTask, LosTaskCB *newTask)
{
UINT64 endTime;
OsSchedSwitchCheck(runTask, newTask); // 检查任务切换的相关条件
runTask->taskStatus &= ~OS_TASK_STATUS_RUNNING; // 清除当前任务的运行状态标志
newTask->taskStatus |= OS_TASK_STATUS_RUNNING; // 设置新任务的运行状态标志
#ifdef LOSCFG_KERNEL_SMP
/* mask new running task's owner processor */
runTask->currCpu = OS_TASK_INVALID_CPUID; // 设置当前任务的 CPU ID 为无效值
newTask->currCpu = ArchCurrCpuid(); // 设置新任务的 CPU ID 为当前 CPU 的 ID
#endif
OsCurrTaskSet((VOID *)newTask); // 设置当前任务为新任务
LosProcessCB *newProcess = OS_PCB_FROM_PID(newTask->processID); // 获取新任务所属的进程
LosProcessCB *runProcess = OS_PCB_FROM_PID(runTask->processID); // 获取当前任务所属的进程
if (runProcess != newProcess) { // 如果当前任务和新任务所属的进程不同
OsSchedSwitchProcess(runProcess, newProcess); // 进行进程切换
}
if (OsProcessIsUserMode(newProcess)) { // 如果新进程是用户态进程
OsCurrUserTaskSet(newTask->userArea); // 设置当前用户态任务为新任务的用户态区域
}
#ifdef LOSCFG_KERNEL_CPUP
OsCpupCycleEndStart(runTask->taskID, newTask->taskID); // 记录当前任务和新任务的 CPU 周期计数
#endif
#ifdef LOSCFG_SCHED_DEBUG
UINT64 waitStartTime = newTask->startTime; // 保存等待调度的起始时间
#endif
if (runTask->taskStatus & OS_TASK_STATUS_READY) {
/* When a thread enters the ready queue, its slice of time is updated */
newTask->startTime = runTask->startTime; // 如果当前任务是就绪状态,则将新任务的起始时间设置为当前任务的起始时间
} else {
/* The currently running task is blocked */
newTask->startTime = OsGetCurrSchedTimeCycle(); // 如果当前任务是阻塞状态,则将新任务的起始时间设置为当前调度时间
/* The task is in a blocking state and needs to update its time slice before pend */
OsTimeSliceUpdate(runTask, newTask->startTime); // 更新当前任务的时间片
if (runTask->taskStatus & (OS_TASK_STATUS_PEND_TIME | OS_TASK_STATUS_DELAY)) {
OsAdd2SortLink(&runTask->sortList, runTask->startTime, runTask->waitTimes, OS_SORT_LINK_TASK); // 将当前任务添加到排序链表中
}
}
if (newTask->policy == LOS_SCHED_RR) {
endTime = newTask->startTime + newTask->timeSlice; // 如果新任务是轮转调度策略,则计算新任务的结束时间
} else {
endTime = OS_SCHED_MAX_RESPONSE_TIME - OS_TICK_RESPONSE_PRECISION; // 如果新任务不是轮转调度策略,则设置新任务的结束时间为最大响应时间
}
OsSchedSetNextExpireTime(newTask->startTime, newTask->taskID, endTime, runTask->taskID); // 设置下一个任务的到期时间
#ifdef LOSCFG_SCHED_DEBUG
newTask->schedStat.waitSchedTime += newTask->startTime - waitStartTime; // 更新新任务的等待调度时间
newTask->schedStat.waitSchedCount++; // 增加新任务的等待调度次数
runTask->schedStat.runTime = runTask->schedStat.allRuntime; // 更新当前任务的运行时间
runTask->schedStat.switchCount++; // 增加当前任务的切换次数
#endif
/* do the task context switch */
OsTaskSchedule(newTask, runTask); // 进行任务上下文切换
}
/*在切换任务之前它首先调用OsSchedSwitchCheck函数来检查任务切换的相关条件。然后它更新当前任务和新任务的运行状态标志
并进行一些其他操作,如设置当前任务、进行进程切换、设置用户态任务等。接下来,它根据任务的状态更新新任务的起始时间,并设置
下一个任务的到期时间。最后它调用OsTaskSchedule函数进行任务上下文切换。
*/
VOID OsSchedIrqEndCheckNeedSched(VOID)
{
Percpu *percpu = OsPercpuGet(); // 获取当前 CPU 的数据结构指针
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
OsTimeSliceUpdate(runTask, OsGetCurrSchedTimeCycle()); // 更新当前任务的时间片
if (runTask->timeSlice <= OS_TIME_SLICE_MIN) { // 如果当前任务的时间片小于等于最小时间片
percpu->schedFlag |= INT_PEND_RESCH; // 设置调度标志,表示需要进行任务切换
}
if (OsPreemptable() && (percpu->schedFlag & INT_PEND_RESCH)) { // 如果可以抢占且需要进行任务切换
percpu->schedFlag &= ~INT_PEND_RESCH; // 清除调度标志
LOS_SpinLock(&g_taskSpin); // 获取任务自旋锁
OsSchedTaskEnQueue(runTask); // 将当前任务加入就绪队列
LosTaskCB *newTask = OsGetTopTask(); // 获取优先级最高的任务
if (runTask != newTask) { // 如果当前任务不是优先级最高的任务
OsSchedTaskSwicth(runTask, newTask); // 进行任务切换
LOS_SpinUnlock(&g_taskSpin); // 释放任务自旋锁
return;
}
LOS_SpinUnlock(&g_taskSpin); // 释放任务自旋锁
}
if (percpu->schedFlag & INT_PEND_TICK) { // 如果有时钟中断挂起
OsSchedUpdateExpireTime(runTask->startTime); // 更新任务的到期时间
}
}
VOID OsSchedResched(VOID)
{
LOS_ASSERT(LOS_SpinHeld(&g_taskSpin)); // 断言任务自旋锁已持有
#ifdef LOSCFG_KERNEL_SMP
LOS_ASSERT(OsPercpuGet()->taskLockCnt == 1); // 断言任务锁计数为1
#else
LOS_ASSERT(OsPercpuGet()->taskLockCnt == 0); // 断言任务锁计数为0
#endif
OsPercpuGet()->schedFlag &= ~INT_PEND_RESCH; // 清除调度标志
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
LosTaskCB *newTask = OsGetTopTask(); // 获取优先级最高的任务
if (runTask == newTask) { // 如果当前任务就是优先级最高的任务
return;
}
OsSchedTaskSwicth(runTask, newTask); // 进行任务切换
}
/*这段代码实现了在中断结束时检查是否需要进行任务切换的函数OsSchedIrqEndCheckNeedSched
以及在调度器需要重新调度时执行任务切换的函数OsSchedResched。
OsSchedIrqEndCheckNeedSched函数首先更新当前任务的时间片并检查当前任务的时间片是否小于
等于最小时间片。如果是,则设置调度标志,表示需要进行任务切换。然后,它检查是否可以抢占且
调度标志已被设置。如果满足条件,则获取任务自旋锁,并将当前任务加入就绪队列。接下来,它获
取优先级最高的任务,如果当前任务不是优先级最高的任务,则进行任务切换,并释放任务自旋锁。
最后,如果有时钟中断挂起,它更新任务的到期时间。
OsSchedResched函数首先断言任务自旋锁已持有并根据配置选项判断任务锁计数是否为1SMP
或者为0非SMP。然后它清除调度标志并获取当前运行的任务和优先级最高的任务。如果当前
任务就是优先级最高的任务,则直接返回。否则,它进行任务切换。*/
VOID LOS_Schedule(VOID)
{
UINT32 intSave;
LosTaskCB *runTask = OsCurrTaskGet(); // 获取当前运行的任务
if (OS_INT_ACTIVE) { // 如果处于中断上下文
OsPercpuGet()->schedFlag |= INT_PEND_RESCH; // 设置调度标志,表示需要进行任务切换
return;
}
if (!OsPreemptable()) { // 如果不允许抢占
return;
}
/*
* trigger schedule in task will also do the slice check
* if necessary, it will give up the timeslice more in time.
* otherwise, there's no other side effects.
*/
SCHEDULER_LOCK(intSave); // 获取调度器锁,禁止调度器抢占
OsTimeSliceUpdate(runTask, OsGetCurrSchedTimeCycle()); // 更新当前任务的时间片
/* add run task back to ready queue */
OsSchedTaskEnQueue(runTask); // 将当前任务加入就绪队列
/* reschedule to new thread */
OsSchedResched(); // 进行任务切换
SCHEDULER_UNLOCK(intSave); // 释放调度器锁
}
STATIC INLINE LOS_DL_LIST *OsSchedLockPendFindPosSub(const LosTaskCB *runTask, const LOS_DL_LIST *lockList)
{
LosTaskCB *pendedTask = NULL;
LOS_DL_LIST *node = NULL;
LOS_DL_LIST_FOR_EACH_ENTRY(pendedTask, lockList, LosTaskCB, pendList) {
if (pendedTask->priority < runTask->priority) {
continue;
} else if (pendedTask->priority > runTask->priority) {
node = &pendedTask->pendList;
break;
} else {
node = pendedTask->pendList.pstNext;
break;
}
}
return node;
}
LOS_DL_LIST *OsSchedLockPendFindPos(const LosTaskCB *runTask, LOS_DL_LIST *lockList)
{
LOS_DL_LIST *node = NULL;
if (LOS_ListEmpty(lockList)) { // 如果锁的等待队列为空
node = lockList;
} else {
LosTaskCB *pendedTask1 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(lockList));
LosTaskCB *pendedTask2 = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_LAST(lockList));
if (pendedTask1->priority > runTask->priority) { // 如果最高优先级的等待任务的优先级高于当前任务的优先级
node = lockList->pstNext;
} else if (pendedTask2->priority <= runTask->priority) { // 如果最低优先级的等待任务的优先级小于等于当前任务的优先级
node = lockList;
} else {
node = OsSchedLockPendFindPosSub(runTask, lockList); // 在等待队列中查找当前任务应该插入的位置
}
}
return node;
}
/*LOS_Schedule函数用于触发调度器进行任务切换。在函数中首先判断是否处于中断上下文中
如果是,则设置调度标志,表示需要进行任务切换,然后返回。接下来,判断当前任务是否可被抢占,
如果不可抢占,则直接返回。然后,获取调度器锁,并更新当前任务的时间片。接着,将当前任务加入就绪队列,
并调用OsSchedResched函数进行任务*/