/*
* 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 ( " \n Tick 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 - >
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 函 数 首 先 断 言 任 务 自 旋 锁 已 持 有 , 并 根 据 配 置 选 项 判 断 任 务 锁 计 数 是 否 为 1 ( SMP )
或 者 为 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 函 数 进 行 任 务 */