当前位置: 首页 > news >正文

SylixOS 调度浅析

1、简介

每个 CPU 如果需要被调度,即进行 CPU 任务上下文切换,有两种情况:主动调度、被动调度

对于多核 CPU 来说,如何通知某个 CPU,它需要被调度?

答:通过 IPI 中断

2、调度时机

2.1、主动调度

主动调度,即 CPU 当前运行的线程主动去调用可能产生调度的相关函数,如

  • API_SemaphoreBRelease、API_SemaphoreBPost 等释放信号量操作
    • 释放信号量时,通常会伴随着唤醒阻塞在这个信号量上的任务
  • Sleep、Usleep 等主动睡眠操作
    • 当前任务主动放弃 CPU 使用权
  • API_SemaphoreBPend、API_SemaphoreCPend 等尝试获取信号量操作
    • 当前任务申请信号量、尝试访问临界区,且临界区已经被占据,这时就产生了阻塞

上述接口,都会直接调用到 _Schedule()或者 _ScheduleInt()函数,进行 CPU 调度。

_Schedule()函数可以分为两部分:

  • 获得需要运行的线程
    • 如果当前 CPU 被设置卷绕标志,说明有线程需要被调度到当前核运行。调用 _CandTableUpdate 更新当前 CPU 候选线程
    • 如果没有被设置卷绕表示,则直接获取当前 CPU 的候选线程

关于 CPU 核的 “卷绕标志”,可以暂时理解为,经过一系列调度算法,准备给目标线程安排一个 CPU 运行,同时给该 CPU 设置卷绕标志,在合适时机给设置了该标志的 CPU 发送 IPI 中断,通知其进行线程调度、线程上下文切换。

  • 如果和 CPU 当前运行线程不同,则进行线程上下文切换;如果和 CPU 当前运行线程相同,则当前 CPU 不需要调度,但是需要向其它 CPU 发送 IPI 中断,以通知其它 CPU 进行调度。当然,并不是向所有 CPU 发送 IPI 中断,只会向被设置了卷绕标志的 CPU 发送 IPI 中断。

IPI 中断为什么会引起调度?因为 SylixOS 内核中任何中断在处理结束、退出时,都会去调用 __KERNEL_SCHED_INT-> _ScheduleInt(),_ScheduleInt()函数中进行中断上下文中线程切换。

2.2、被动调度

被动调度又可以分为两种情况

  • IPI 中断:当有其他高优先级线程需要被安排到本核,即会向本核发出核间中断,中断退出时,调用 _ScheduleInt 切换到高优先级线程。
  • Tick 中断(RR 调度策略情况下):_SchedTick 函数,会循环检查每个 CPU 当前运行的线程的时间片是否耗尽,如果耗尽,则对当前 CPU 设置卷绕标志。在 Tick 中断退出时,调用的 _ScheduleInt,向这些被设置卷绕标志的 CPU 发送 IPI 中断。
    • 在调用 _CandTableNext 接口,从就绪表中确定一个最需运行的线程时,如果找到的是一个时间片已经用完的线程,则为其补充时间片,同时跳过该线程,寻找下一个。

2.3 任务状态

  SylixOS 支持三种任务状态,分别是就绪态运行态阻塞态
在这里插入图片描述
  这其中,阻塞态,又可以详细分为等待和休眠

  • 等待:任务等待某个条件或事件发生(例如信号量、消息队列、互斥锁、事件标志、tick 等)
  • 休眠:入休眠态是任务的主动过程,这主要是任务调用了内核提供的休眠函数(例如 sleep、delay 等)
    在这里插入图片描述

3、任务调度链表

3.1 任务就绪表

/*********************************************************************************************************位图表
*********************************************************************************************************/
#ifdef __SYLIXOS_KERNELtypedef struct {volatile UINT32       BMAP_uiMap;                                   /*  主位图掩码                  */volatile UINT32       BMAP_uiSubMap[(LW_PRIO_LOWEST >> 5) + 1];     /*  辅位图掩码                  */
} LW_CLASS_BMAP;
typedef LW_CLASS_BMAP    *PLW_CLASS_BMAP;/*********************************************************************************************************优先级控制块
*********************************************************************************************************/typedef struct {LW_LIST_RING_HEADER   PCB_pringReadyHeader;                         /*  就绪非运行线程环表          */UINT8                 PCB_ucPriority;                               /*  优先级                      */
} LW_CLASS_PCB;
typedef LW_CLASS_PCB     *PLW_CLASS_PCB;/*********************************************************************************************************就绪表
*********************************************************************************************************/typedef struct {LW_CLASS_BMAP         PCBM_bmap;LW_CLASS_PCB          PCBM_pcb[LW_PRIO_LOWEST + 1];
} LW_CLASS_PCBBMAP;
typedef LW_CLASS_PCBBMAP *PLW_CLASS_PCBBMAP;

  SylixOS 系统启动的过程会初始化一个任务优先级就绪表,如果是单核系统,初始化的是全局就绪表 _K_pcbbmapGlobalReady 。多核的话,除了全局就绪表,把每一个核对应的本地就绪表 &((pcpu)->CPU_pcbbmapReady 初始化。根据任务是否绑核、CPU 是否设置强亲和性,加入到对应 CPU 核的优先级就绪表。

/*********************************************************************************************************全局就绪位图表
*********************************************************************************************************/
__KERNEL_EXT LW_CLASS_PCBBMAP        _K_pcbbmapGlobalReady;

  就绪表可以分为两部分:

  • 位图表

  • 优先级控制块
    在这里插入图片描述

  • 位图表的作用,就是找到所有就绪线程中,最高的优先级号码

  • 根据优先级号码(也就是优先级控制块的索引),寻找指定的优先级控制块

  • 根据优先级控制块,就能找到运行在该优先级下的所有线程(TCB)

下面讲解几个关键函数:

  _BitmapHigh 函数,就是从指定位图表中,找到最高优先级号码。

/*********************************************************************************************************
** 函数名称: _BitmapHigh
** 功能描述: 获得位图表中的最高优先级
** 输 入  : pbmap         位图控制块
** 输 出  : 优先级
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
UINT8  _BitmapHigh (PLW_CLASS_BMAP  pbmap)
{UINT32  uiHigh = (UINT32)archFindLsb((INT)pbmap->BMAP_uiMap);UINT32  uiLow;if (uiHigh == 0) {return  (0);}uiHigh--;uiLow = (UINT32)archFindLsb((INT)pbmap->BMAP_uiSubMap[uiHigh]) - 1;return  ((UINT8)((uiHigh << 5) | uiLow));
}

  _GetPcb 函数,获取优先级控制块。根据线程是否绑核,选择获取的是全局优先级控制块还是指定 CPU 的优先级控制块。从这里也可以看到,不绑核的情况下,默认都是获取的全局优先级控制块。

static LW_INLINE PLW_CLASS_PCB  _GetPcb (PLW_CLASS_TCB  ptcb)
{
#if LW_CFG_SMP_EN > 0if (ptcb->TCB_bCPULock) {                                           /*  锁定 CPU                    *//* 寻找指定 CPU 的优先级控制块 */return  (LW_CPU_RDY_PPCB(LW_CPU_GET(ptcb->TCB_ulCPULock), ptcb->TCB_ucPriority));} else 
#endif                                                                  /*  LW_CFG_SMP_EN > 0           */{/* 寻找全局优先级控制块 */return  (LW_GLOBAL_RDY_PPCB(ptcb->TCB_ucPriority));}
}

  以 _CandTableSeek 函数为例,讲解从就绪表中,找到优先级最高的那个,返回值为就绪表

/*********************************************************************************************************
** 函数名称: _CandTableSeek
** 功能描述: 调度器查询有就绪线程的最高优先级内联函数.
** 输 入  : pcpu              CPU
**           pucPriority       优先级返回值
** 输 出  : 就绪表.
** 全局变量: 
** 调用模块: 
*********************************************************************************************************/
static LW_INLINE PLW_CLASS_PCBBMAP  _CandTableSeek (PLW_CLASS_CPU  pcpu, UINT8 *pucPriority)
{
#if LW_CFG_SMP_EN > 0REGISTER UINT8  ucGlobal;if (_BitmapIsEmpty(LW_CPU_RDY_BMAP(pcpu))) {/* 当前核就绪表为空 *//* 如果当前 CPU 被设置强亲和性,或者全局优先级就绪表为空 */if (LW_CPU_ONLY_AFFINITY_GET(pcpu) ||_BitmapIsEmpty(LW_GLOBAL_RDY_BMAP())) {                     /*  就绪表为空                  */return  (LW_NULL);}*pucPriority = _BitmapHigh(LW_GLOBAL_RDY_BMAP());return  (LW_GLOBAL_RDY_PCBBMAP());                              /*  从全局就绪表选择            */} else {/* 当前核就绪表不为空,获取当前 CPU 就绪表中的最高优先级 */*pucPriority = _BitmapHigh(LW_CPU_RDY_BMAP(pcpu));              /*  本地就绪表最高优先级获取    *//* 如果当前 CPU 被设置强亲和性,或者全局优先级就绪表为空 */if (LW_CPU_ONLY_AFFINITY_GET(pcpu) || _BitmapIsEmpty(LW_GLOBAL_RDY_BMAP())) {return  (LW_CPU_RDY_PCBBMAP(pcpu));                         /*  选择本地就绪任务            */}/* 获取全局优先级就绪表中的最高优先级 */ucGlobal = _BitmapHigh(LW_GLOBAL_RDY_BMAP());if (LW_PRIO_IS_HIGH_OR_EQU(*pucPriority, ucGlobal)) {           /*  同优先级, 优先执行 local    */return  (LW_CPU_RDY_PCBBMAP(pcpu));} else {*pucPriority = ucGlobal;return  (LW_GLOBAL_RDY_PCBBMAP());}}#else/* 单核情况,直接从全局优先级控制块中寻找 */if (_BitmapIsEmpty(LW_GLOBAL_RDY_BMAP())) {                         /*  就绪表中无任务              */return  (LW_NULL);} else {*pucPriority = _BitmapHigh(LW_GLOBAL_RDY_BMAP());return  (LW_GLOBAL_RDY_PCBBMAP());}
#endif                                                                  /*  LW_CFG_SMP_EN > 0           */
}

找到就绪表后,调用 _CandTableNext 函数,从指定就绪表中获取最需要运行的线程

/*********************************************************************************************************
** 函数名称: _CandTableNext
** 功能描述: 从就绪表中确定一个最需运行的线程.
** 输 入  : ppcbbmap          就绪表
**           ucPriority        优先级
** 输 出  : 在就绪表中最需要运行的线程.
** 全局变量: 
** 调用模块: 
*********************************************************

文章转载自:

http://lyf3eHfv.LhLdx.cn
http://s8eUh0ZO.LhLdx.cn
http://pxQvO9pZ.LhLdx.cn
http://hZeqshJv.LhLdx.cn
http://dnCzAIct.LhLdx.cn
http://c93cjE3I.LhLdx.cn
http://5AcOQ5ZY.LhLdx.cn
http://4KxrLakY.LhLdx.cn
http://YtwqBHah.LhLdx.cn
http://bcC6k9Ef.LhLdx.cn
http://IbPyoX3w.LhLdx.cn
http://T9EScpUY.LhLdx.cn
http://1EpQAwhE.LhLdx.cn
http://X9DtzU8d.LhLdx.cn
http://bcz0GZeg.LhLdx.cn
http://qmkoBFzg.LhLdx.cn
http://Gg3Yith7.LhLdx.cn
http://T3qdVdoW.LhLdx.cn
http://CFG6GIs7.LhLdx.cn
http://MC0sdeSm.LhLdx.cn
http://lAxgpCAK.LhLdx.cn
http://YasNeLyi.LhLdx.cn
http://593wMYoJ.LhLdx.cn
http://8Izi4dpZ.LhLdx.cn
http://v5xUQwaA.LhLdx.cn
http://KXkPoZic.LhLdx.cn
http://108Kw8bM.LhLdx.cn
http://xfCk1hCV.LhLdx.cn
http://Wl7wGQbL.LhLdx.cn
http://reKKalsT.LhLdx.cn
http://www.dtcms.com/a/371855.html

相关文章:

  • 1.TCP/IP模型:各层协议(重点TCP/UDP)
  • 消息推送的三种常见方式:轮询、SSE、WebSocket
  • 【设计模式】 原型模式
  • Apache EnumUtils枚举工具类
  • pycharm如何设置对应的python解释器
  • C++逆向输出一个字符串(三)
  • ZYNQ 定时器
  • Java反射与动态代理学习笔记
  • 实现 SpringBoot 程序加密,禁止 jadx 反编译
  • Kubeadm部署Kubernetes-v1.30.1【容器运行时containerd】
  • HOT100--Day14--543. 二叉树的直径,102. 二叉树的层序遍历,108. 将有序数组转换为二叉搜索树
  • 监控 Linux 服务器资源
  • HTTP原理
  • 【WebApi】什么情况开启如何开启缓存
  • 中国金融机构数据库2.0-许可证、机构设立、退出、失控信息2007-2023.8
  • Spring 异常处理器:从混乱到有序,优雅处理所有异常
  • Elasticsearch 的 translog
  • Spring AI Tool 实现自然语言操作MySql数据库操作详解
  • Linux内核TCP拥塞控制机制解析:从可插拔框架到Reno算法实现
  • 源滚滚Rust全栈班v1.02 无符号整数详解
  • 2025最新超详细FreeRTOS入门教程:第四章 FreeRTOS消息队列
  • Rust 登堂 之 Drop 释放资源(十一)
  • 开关电源的原理、结构和实物入门篇-超简单解读
  • Environments
  • 上架商品合规流程有多条,有的长,有的短,有的需要审核,校验商品的合规性
  • 简单聊一聊js
  • 合格齿轴工艺工程师要修炼哪些功法?
  • LwIP入门实战 — 5 LwIP 的内存管理
  • 【三维生成】Matrix-3D:全向可探索的三维世界生成
  • DispatcherServlet 初始化过程:SpringMVC 的 “启动引擎” 详解