调度扩展:调度器架构和接口(第 2 部分)

这是关于 sched_ext(基于 BPF 的可扩展调度器类)的第二篇博客文章。

现在正在发生的事情:经过关于 sched_ext 补丁的长时间讨论,Linus 最终批准将其纳入 6.11 内核,V7 补丁集已发布到邮件列表,sched_ext 的开发者和用户社区在不断扩大,更多开发者参加每周办公时间,许多爱好者也在积极测试、基准测试和报告 sched_ext 调度器的问题。

开始开发 sched_ext 调度器的地方:有三种获取支持 sched_ext 内核的方式,包括安装默认支持的 Linux 发行版(如 CachyOS)、使用虚拟机(如 virtme_ng)或自行构建内核并设置正确的配置选项。

理解 Linux 调度器的全貌:Linux 调度器概念上分为两层,核心内核调度器(core.c)定义所有调度策略的通用行为,具体调度策略(如实时调度策略和公平调度策略)基于核心内核调度器定义,sched_ext 框架(ext.c)像其他调度器类一样定义在核心内核调度器之上,sched_ext 框架是一个承载带有用户定义策略的 BPF 调度器的容器,包含 BPF 调度器层和与 BPF 调度器交互的用户空间进程,编写新的 sched_ext 调度器意味着编写新的 BPF 调度器及其用户空间对应部分。

共享时间与共享空间的不同:调度器类与 VFS(虚拟文件系统)层的概念有些相似,但调度是关于如何分割和使用 CPU 时间,在某一时刻只有一个调度策略可以进行调度决策,实时调度器优先获取 CPU 时间,sched_ext 基于的调度器会接管所有普通类任务。

接口很重要:sched_ext 调度器由核心内核调度器、sched_ext 框架、BPF 调度器和 BPF 的用户空间对应部分四层组成,通过相对明确的接口相互交互。

  • 接口 1:核心内核调度器⇒调度器类:核心内核调度器定义调度的通用底层行为并为具体调度器类定义接口,sched_ext 框架实现struct sched_class中的函数,为 BPF 调度器提供通用实现。
  • 接口 2:sched_ext 框架⇒BPF 调度器enqueue_task_scx()函数的设计取决于 BPF 调度器的调度策略,sched_ext 框架定义struct sched_ext_ops,其中scx_ops.enqueue()等函数用于与 BPF 调度器交互,BPF 调度器在加载和卸载时可通过sched_ext_ops.init()sched_ext_ops.exit()函数初始化和清理数据结构,在任务创建和终止时分别调用sched_ext_ops.init_task()sched_ext_ops.exit_task(),在任务的不同状态转换时调用相应的回调函数。
  • 接口 3:BPF 调度器⇒sched_ext 框架:BPF 调度器通过 BPF 辅助函数(如创建和销毁调度队列(DSQ)、调度任务等)与 sched_ext 框架通信,DSQ 是 BPF 调度器和 sched_ext 框架之间的核心结构,BPF 调度器可创建自己的 DSQ 来管理可运行任务,任务可按到达顺序或vtime顺序入队和出队,还可通过其他辅助函数选择 CPU 和发送 IPI 信号。
  • 接口 4:BPF 调度器⟺用户空间对应部分:BPF 调度器是常规 BPF 程序,用户空间程序可使用libbpf API 访问和操作 BPF 映射。

接下来做什么:最好的理解 sched_ext 概念的方式是阅读现有 BPF 调度器代码,如scx_lavdscx_rustyscx_rustland的源代码,下一篇博客将讨论作为scx_lavd调度器的作者如何改进。

阅读 30
0 条评论