news 2026/6/18 16:51:59

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 的成对设计
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/18 16:28:40

【分享】To do list1.02.99高级版[特殊字符]时间高效管理

【软件介绍】To Do List手机版是一款简洁易用,专注高效的待办事项、时间管理的效率类应用,相信大家在日常生活中,每天都有许多待办事件,而且常常也都会忙的忘记某件事情,那么这时就可以配合该软件来进行使用啦&#xf…

作者头像 李华
网站建设 2026/6/18 16:29:50

硬件工程师七年成长:从零搭建到职场实战的硬核自学之路

1. 从实验室到职场:一个硬件工程师的七年成长路七年前,我拖着行李箱离开那个北方小城,心里揣着对电子电路模糊的向往,踏上了求学路。七年后的今天,我坐在自己租住的公寓里,面前摊着还没画完的PCB板&#xf…

作者头像 李华
网站建设 2026/6/18 16:27:36

3步搞定鸣潮自动化:让AI助手帮你解放双手

3步搞定鸣潮自动化:让AI助手帮你解放双手 【免费下载链接】ok-wuthering-waves 鸣潮 后台自动战斗 自动刷声骸 一键日常 Automation for Wuthering Waves 项目地址: https://gitcode.com/GitHub_Trending/ok/ok-wuthering-waves 还在为《鸣潮》中重复刷本、日…

作者头像 李华
网站建设 2026/6/17 2:23:26

Mythos Preview:AI安全能力跃迁与可控释放的工程实践

1. 项目概述:一场静默却震耳欲聋的AI能力跃迁这周,整个AI安全圈没有爆炸性新闻稿,没有铺天盖地的发布会直播,只有一份措辞克制、数据密集的系统卡片(System Card)和一份由英国AI安全研究所(AISI…

作者头像 李华
网站建设 2026/6/17 3:41:13

问卷填报名表油猴脚本

仅针对特定问卷// UserScript // name 问卷自动填写 // match https://v.wjx.cn/vm/tTolo5.aspx* // grant none // run-at document-idle // /UserScript(function () {use strict;// 按顺序填你的答案,几个填空写几个const ANSWERS ["张三…

作者头像 李华