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

Sched_ext 回调深度解析(一):sched_ext 框架总览——前言

基于 Linux 6.18.26,结合内核源码逐行分析

系列文章:

  • Sched_ext 回调深度解析(一):sched_ext 框架总览——前言
  • Sched_ext 回调深度解析(二):init_task —— 每个任务走进调度器的第一道门(6.18.26)
  • Sched_ext 回调深度解析(三):enable —— 任务被调度器接管的关键时刻(6.18.26)
  • Sched_ext 回调深度解析(四):select_cpu —— 任务唤醒时的选核决策(6.18.26)
  • Sched_ext 回调深度解析(五):runnable —— 任务状态转换的哨兵(6.18.26)
  • Sched_ext 回调深度解析(六):enqueue —— 任务入队,调度器的核心决策点(6.18.26)
  • Sched_ext 回调深度解析(七):dispatch —— 从队列取出任务送到 CPU(6.18.26)
  • Sched_ext 回调深度解析(八):running —— 任务开始执行(6.18.26)

1. sched_ext 是什么

sched_ext(简称 scx)是 Linux 内核的 eBPF 调度器框架,自 6.12 起合入主线。它允许开发者用 eBPF 程序在用户态编写自定义 CPU 调度策略,而无需修改内核代码或重启系统。

传统上,添加一个新的 Linux 调度器意味着编写一个完整的sched_class,直接操作内核内部数据结构——这需要内核开发 expertise,修改后必须重新编译、重启才能生效。sched_ext改变了这个局面:内核提供了一个标准的回调接口(struct sched_ext_ops),开发者只需用 BPF 程序实现这些回调,就能定义一个完整的调度策略。调度器程序可以热加载、热卸载,切换策略时系统中的任务会自动平滑迁移。

可以把sched_ext理解为一个"调度器插件系统"——内核在关键调度时机(任务唤醒、入队、出队、开始执行、让出 CPU 等)调用 BPF 回调,BPF 程序在回调中做出调度决策。

1.1 框架总览

内核空间

用户空间

sched_ext 框架 (kernel/sched/ext.c)

实现 ops 回调

加载/卸载 BPF 程序

读写

查询/更新

调用 sched_class 方法

触发 ops 回调

ext_sched_class

enqueue_task_scx

dequeue_task_scx

balance_scx

pick_task_scx

put_prev_task_scx → stopping

set_next_task_scx → running

BPF 调度器程序
(实现 sched_ext_ops)

用户态工具
(scx CLI / 自定义工具)

struct sched_ext_ops
回调钩子集合

DSQ 调度队列
(Dispatch Queues)

任务状态机
(scx_task_state)

内核核心调度器
(kernel/sched/core.c)

从上图可以看出整个框架的运作方式:

  1. 开发者编写 BPF 调度器程序,实现struct sched_ext_ops中定义的回调函数。
  2. 用户态工具将 BPF 程序加载到内核,内核注册这些回调。
  3. 内核核心调度器在调度事件发生时,调用ext_sched_class中对应的方法。
  4. ext_sched_class的方法触发 BPF 回调,BPF 程序在回调中做出调度决策(如选择 CPU、入队到哪个 DSQ、从哪个 DSQ 取出任务等)。
  5. **DSQ(Dispatch Queue)**是 BPF 调度器和内核之间的任务交换枢纽——BPF 程序将任务放入 DSQ,内核从 DSQ 中取出任务送到 CPU。

2. 完整回调总览

2.1 回调一览表

struct sched_ext_ops代表一个 BPF 调度器,其中定义了多个回调函数。下表列出了所有核心回调:

钩子触发次数含义
init_task每个 task 仅一次“登记”——task 被 scx 框架认识
enable每个 task 仅一次“上岗”——task 被 scx 正式接管
select_cpu每次唤醒时“选座”——task 醒来时选择目标 CPU
runnable每次变为可运行时“就位”——通知 task 变为 runnable
enqueue每次入队时“排队”——task 被放入调度队列
dispatch每次 CPU 需要任务时“叫号”——从 DSQ 取出 task 送到 CPU
running每次调度执行时“开工”——task 即将占用 CPU
stopping每次调度结束时“收工”——task 让出 CPU
quiescent每次变为不可运行时“离场”——通知 task 变为 quiescent
set_weight权重变更时通知 BPF 调度器 task 的权重
set_cpumaskCPU 亲和性变更时通知允许运行的 CPU 集合

2.2 回调分类

一次性回调(每个 task 仅触发一次):

  • init_task— 登记,task 被 scx 框架认识
  • enable— 上岗,task 被 scx 正式接管

状态通知回调(每次状态变化时触发):

  • runnable/quiescent— 可运行 / 不可运行
  • running/stopping— 开始执行 / 停止执行
  • set_weight/set_cpumask— 权重 / 亲和性变更

调度决策回调(核心调度逻辑):

  • select_cpu— 选核决策,选择目标 CPU
  • enqueue— 入队决策,放入 DSQ
  • dispatch— 分发决策,从 DSQ 取出送到 CPU

3. task 生命周期中的回调时序

创建阶段init_task登记进入scx 框架enable被 scx正式接管每次唤醒select_cpu选择目标CPUrunnable通知 task可运行enqueue放入调度队列每次调度dispatch从 DSQ取出 task送到 CPUrunning开始占用CPU...执行任务...stopping让出 CPU销毁disable脱离 scx管理task 生命周期中的 scx 回调时序

3.1 一次性回调 vs 重复回调

仅一次

仅一次

循环

ops.init_task()

ops.enable()

enqueue / dequeue

ops.dispatch()

ops.running()

task 在 CPU 上执行

ops.stopping()

init_taskenable是"一次性门禁",只在 task 进入 scx 管理时各触发一次;而runningstopping是"旋转门",每次 task 被调度到 CPU 或让出 CPU 时都会触发,构成调度循环。


4. task 的四种状态

每个 task 在 scx 框架中都有一个状态,记录在task->scx.state中:

// include/linux/sched/ext.henumscx_task_state{SCX_TASK_NONE,// ops.init_task() 还没被调用SCX_TASK_INIT,// init_task 执行成功,但任务还没就绪SCX_TASK_READY,// 完全初始化,可以被 scx 调度SCX_TASK_ENABLED,// 已激活,正在被 scx 调度SCX_TASK_NR_STATES,};

状态流转图:

任务创建

init_task 成功返回

注册时 第一轮遍历
fork时 scx_post_fork

sched_class == ext
switching_to_scx 或 scx_post_fork 中 enable

sched_class != ext,保持 READY

调度器卸载或策略切换

SCX_TASK_NONE

SCX_TASK_INIT

SCX_TASK_READY

SCX_TASK_ENABLED

状态含义
SCX_TASK_NONE初始状态,还未被 scx 认识
SCX_TASK_INIT短暂中间态,init_task返回 0 后立即进入
SCX_TASK_READY完全初始化,可以被 scx 调度
SCX_TASK_ENABLED激活态,正在被 scx 调度,ops.enable()已调用

SCX_TASK_INIT是一个中间态,只在scx_init_task()执行成功后短暂存在,紧接着就会被推进到 READY。


5. ext_sched_class 全貌

sched_ext在内核中注册为ext_sched_class,它是完整的 Linux 调度类(sched_class)。内核核心调度器在调度事件发生时,统一通过sched_class的方法指针来调用 scx 的实现。为方便后续各篇分析时对照,这里给出ext_sched_class的完整定义(kernel/sched/ext.c:3324):

DEFINE_SCHED_CLASS(ext)={.enqueue_task=enqueue_task_scx,.dequeue_task=dequeue_task_scx,.yield_task=yield_task_scx,.yield_to_task=yield_to_scx,.wakeup_preempt=wakeup_preempt_scx,.balance=balance_scx,.pick_task=pick_task_scx,.put_prev_task=put_prev_task_scx,// ← stopping 在这里触发.set_next_task=set_next_task_scx,// ← running 在这里触发.select_task_rq=select_task_rq_scx,.task_woken=task_woken_scx,.set_cpus_allowed=set_cpus_allowed_scx,.rq_online=rq_online_scx,.rq_offline=rq_offline_scx,.task_tick=task_tick_scx,.switching_to=switching_to_scx,.switched_from=switched_from_scx,.switched_to=switched_to_scx,.reweight_task=reweight_task_scx,.prio_changed=prio_changed_scx,.update_curr=update_curr_scx,#ifdefCONFIG_UCLAMP_TASK.uclamp_enabled=1,#endif};

注意两个关键点:

  1. ext_sched_class没有pick_next_task方法,只有pick_task。这意味着在__pick_next_task中,scx 走的是class->pick_task(rq)+put_prev_set_next_task()分支,而不是class->pick_next_task(rq, prev)分支。
  2. running 和 stopping 分别挂接在set_next_taskput_prev_task。这两个方法在每次调度切换时成对调用:先put_prev_task(旧任务 stopping),再set_next_task(新任务 running)。

6. 小结与系列导航

本文从宏观角度描绘了sched_ext框架的全貌:

  • 架构层面:sched_ext 是一个 eBPF 驱动的调度器插件系统,用户态 BPF 程序通过实现struct sched_ext_ops中的回调来定义调度策略,内核在关键调度时机调用这些回调。
  • 回调体系:11 个核心回调分为三类——一次性回调(init_taskenable)、状态通知回调(runnable/quiescentrunning/stopping等)和调度决策回调(select_cpuenqueuedispatch)。
  • 状态机:task 在 scx 框架中经历NONE → INIT → READY → ENABLED四个状态,其中INIT是短暂中间态,ENABLED是正常运行态。
  • 调度类集成ext_sched_class作为标准的 Linuxsched_class注册到内核,核心调度器通过统一接口调用 scx 的实现。

理解了框架全貌之后,后续文章将逐一深入每个回调的内核实现细节。建议按以下顺序阅读:

  1. init_task—— 每个 task 走进调度器的第一道门,理解 task 如何被 scx 框架"登记"
  2. enable—— task 被 scx 正式接管的关键时刻,理解状态从 READY 到 ENABLED 的跃迁
  3. select_cpu—— 任务唤醒时的选核决策,理解 BPF 调度器如何影响 CPU 选择
  4. runnable—— 任务状态转换的哨兵,理解 runnable/quiescent 的对称设计
  5. enqueue—— 任务入队,理解 DSQ 机制和调度决策的核心逻辑
  6. dispatch—— 从 DSQ 取出任务送到 CPU,理解分发机制的完整流程
  7. running—— 任务开始执行,理解 running/stopping 的成对设计
http://www.jsqmd.com/news/961705/

相关文章:

  • 论文投稿救星:Word公式一键转MathType的保姆级教程(附omml2mml.xsl报错终极解法)
  • IMX6ULL_主界面
  • 深圳本地黄金回收收的顶靠谱,三十年实体老店,水贝源头价高价收金 - 奢侈品回收测评
  • Figma中文界面汉化终极指南:3步实现设计工具无障碍化
  • FPGA入门:从50MHz时钟分频到1秒LED闪烁的完整设计解析
  • 掌握池化的原理
  • Altium Designer极坐标栅格实战:环形PCB布局效率提升指南
  • 讲真的2026年淄博保险纠纷律师推荐 5位实战经验丰富 - 本地品牌推荐
  • 2026年6月郴州贵金属奢侈品回收指南:郴奢汇万宝店领衔,正规机构推荐清单 - 小仙贝贝
  • MATLAB一键实现正态信息扩散与核密度拟合(含可视化与参数调节)
  • Matlab图像去噪效果量化评估工具:PSNR/SSIM/RMSE一键计算脚本合集
  • 硬件测试工程师:从“打杂”到专业“找茬人”的核心能力与实战指南
  • NLP数据扰动:提升模型鲁棒性的输入空间正则化实践
  • 【2024最新权威认证】:CSDN AI数字营销服务站内广告投放能力白皮书(含API文档截图与合同条款原文)
  • 空调维修培训机构怎么选?零基础入门必看攻略 - 湖南阳光技术
  • YOLO11/12/26/DEIM/RTDETR:选择性频率残差门控SFRG(自研独家),通过“低频看全局、高频看细节”的互补共振,把小目标成倍放大、把遮挡部分用全局语义补齐、抑背景噪声。
  • 机器学习第4周:猴痘病识别
  • AI Slop 正在吞噬互联网:当生成式泛滥成为技术社区的隐形杀手
  • BEVFormer TensorRT部署工具包:含INT8量化流程、CUDA自定义算子源码与Docker一键构建环境
  • FOC轮腿机器人:嵌入式运动控制系统的技术实现与创新
  • MuleSoft与LangChain协同架构:企业级AI中台的工程实践
  • 2026 芜湖防水补漏瓷砖空鼓修复推荐,苏易修缮本土直营,长江圩区汛期返渗皖南丘陵山泉渗水梅季高湿闷泡冬夏温差裂漏软基沉降翘砖就近微创修 - 苏易修缮
  • LabVIEW串口调试助手开发:从数据流原理到工程实践
  • Atom简体中文汉化包:为开发者打造无障碍编程体验的本地化解决方案
  • 2026 铜陵防水补漏瓷砖空鼓修复推荐,苏易修缮本土直营,长江汛期江水顶渗皖南超长梅雨矿区岩溶沉降南部丘陵山体渗水冬夏温差开裂沿江滨湖软基翘砖就近微创修 - 苏易修缮
  • 8D报告怎么写
  • 深度解析FOC轮腿机器人:从零构建智能平衡机器人的完整攻略
  • MATLAB版LDPC码实战包:从随机H矩阵生成到LLR-BP译码及BER曲线绘制
  • 电源设计实战宝典:从EMC、安规到PCB布局的工程师指南
  • 上市智慧食堂厂家盘点:从资质到落地的客观对比 - 互联网科技品牌测评