关于linux:龙蜥开源Plugsched首次实现-Linux-kernel-调度器热升级-龙蜥技术

36次阅读

共计 5919 个字符,预计需要花费 15 分钟才能阅读完成。

简介:对于 plugsched 而言,无论是 bugfix,还是性能优化,甚至是个性的增、删、改,都可胜任。

文 / 龙蜥社区内核开发人员 陈善佩、吴一昊、邓二伟

Plugsched 是 Linux 内核调度器子系统热降级的 SDK,它能够实现在不重启零碎、利用的状况下动静替换调度器子系统,毫秒级 downtime。Plugsched 能够对生产环境中的内核调度个性动静地进行增、删、改,以满足不同场景或利用的需要,且反对回滚。

基于 plugsched 实现的调度器热降级,不批改现有内核代码,就能取得较好的可批改能力,人造反对线上的老内核版本。如果提前在内核调度器代码的要害数据结构中退出 Reserve 字段,能够额定取得批改数据结构的能力,进一步晋升可批改能力。

Plugsched 开源链接:https://gitee.com/anolis/plug…

  • 那么 Plugsched 诞生的背景或者想要解决的问题是什么?咱们认为有以下 4 点:
  • 利用场景不同,最佳调度策略不同。利用品种极大丰富,利用特色也是变幻无穷(throughput-oriented workloads, 𝜇s-scale latency critical workloads, soft real-time, and energy efficiency requirements),使得调度策略的优化比较复杂,不存在“一劳永逸”的策略。因而,容许用户定制调度器满足不同的场景是必要的。
  • 调度器迭代慢。Linux 内核通过很多年的更新迭代,代码变得越来越沉重。调度器是内核最外围的子系统之一,它的结构复杂,与其它子系统严密耦合,这使得开发和调试变得越发艰难。此外,Linux 很少减少新的调度类,尤其是不太可能承受非通用或场景针对型的调度器,上游社区在调度畛域发展缓慢。
  • 内核降级艰难。调度器内嵌(built-in)在内核中,上线调度器的优化或新个性须要降级内核版本。内核公布周期通常是数月之久,这将导致新的调度器无奈及时利用在生产零碎中。再者,要在集群范畴降级新内核,波及业务迁徙和停机降级,对业务方来说代价低廉。
  • 无奈降级子系统。kpatch 和 livepatch 是函数粒度的热降级计划,可批改能力较弱,不能实现简单的逻辑改变;eBPF 技术在内核网络中广泛应用,但当初调度器还不反对 ebpf hook,未来即便反对,也只是实现部分策略的灵便批改,可批改能力同样较弱。

Plugsched 能将调度器子系统从内核中提取进去,以模块的模式对内核调度器进行热降级。通过对调度器模块的批改,可能针对不同业务定制化调度器,而且应用模块可能更麻利的开发新个性和优化点,并且能够在不中断业务的状况下上线。


图 1 plugsched: 业务不中断

应用 plugsched 具备以下 6 大劣势:

  • 与内核公布解耦:调度器版本与内核版本解耦,不同业务能够应用不同调度策略;建设继续运维能力,减速调度问题修复、策略优化落地;晋升调度畛域翻新空间,放慢调度器技术演进和迭代
  • 可批改能力强:能够实现简单的调度个性和优化策略,能人之所不能
  • 保护简略:不批改内核代码,或大量批改内核代码,放弃内核主线洁净整洁;在内核代码 Tree 外独立保护非通用调度策略,采纳 RPM 的模式公布和上线
  • 简略易用:容器化的 SDK 开发环境,一键生成 RPM,开发测试简洁高效
  • 向下兼容:反对老内核版本,使得存量业务也能及时享受新技术红利
  • 高效的性能:毫秒级 downtime,可疏忽的 overhead。

Plugsched 利用案例

Plugsched 相比 kpatch 和 livepatch 可批改能力更强,热降级范畴更广,plugsched 是子系统范畴的热降级,而后者是函数级别的热降级。对于 plugsched 而言,无论是 bugfix,还是性能优化,甚至是个性的增、删、改,都可胜任。鉴于 plugsched 较强的可批改能力,它可利用到以下场景:

  • 疾速开发、验证、上线新个性,稳固后放入内核主线
  • 针对不同业务场景做定制优化,以 RPM 包的模式公布和保护非通用调度器个性
  • 对立治理调度器热补丁,防止多个热补丁之间的抵触而引发故障

利用案例 1:新增 Group Identity 调度个性

Group Identity 是阿里云用于混部场景的调度个性,它基于 CFS 调度器减少了一颗存储低优先级工作的红黑树,而且会对每一个 cgroup 调配一个默认优先级,用户也可自行配置其优先级,当队列中存在高优先级工作时,低优先级工作会进行运行。咱们利用 plugsched 对 anck 4.19 的一个老版本内核(没有该调度个性)进行调度器热降级,并将 Group Identity 调度个性移植到生成的调度器模块中,波及 7 个文件,2500+ 行的批改量。

装置该调度器模块后,在零碎中创立两个 cpu cgroup A 和 B,并绑定同一个 CPU,别离设置最高和最低优先级,而后各自创立一个 busy loop 工作。实践上,当 A 中有工作执行时,B 中的工作会进行运行。此时用 top 工具查看该 CPU 利用率,发现只有一个利用率是 100% 的 busy loop 工作,阐明模块中的 Group Identity 个性已失效;而动静卸载该模块后,呈现了两个各占 50% CPU 的 busy loop 工作,阐明模块曾经生效。

利用案例 2:与内核公布解耦及定制化调度器

阿里云某客户应用的旧版本内核,因为该内核调度器对 load 的统计算法不合理,导致 CPU 利用率过高,尽管修复补丁曾经合入内核主线,然而新内核版本还未公布,而且业务方也不打算更换内核,因为集群中部署了大量的业务,降级内核老本较高。

除此之外,客户的内核开发人员对其混部业务场景(Group Identity 调度个性)进行了针对性的优化,想将优化内容合入内核主线。然而,阿里云内核开发人员发现,该优化内容在其它场景中有性能回退,属于非通用优化,因而不容许将优化内容合入主线。于是,客户的内核开发人员应用 plugsched 将优化修复内容全副移植到调度器模块中,最初规模部署。该案例能够体现出 plugsched 的与内核公布解耦、定制化调度器的劣势。

那么 Plugsched 该如何应用?

目前,plugsched 默认反对 Anolis OS 7(内核 ANCK-4.19 版本)零碎,其它操作系统须要调整边界配置。为了加重搭建运行环境的复杂度,咱们提供了容器镜像和 Dockerfile,开发人员不须要本人去搭建开发环境。为了不便演示,这里购买了一台阿里云 ECS(64CPU + 128GB),并装置 Anolis OS 7.9 ANCK 零碎发行版,咱们将演示对其内核调度器进行热降级的过程。

1、登陆云服务器后,先装置一些必要的根底软件包:

# yum install anolis-repos -y # yum install podman kernel-debuginfo-$(uname -r) kernel-devel-$(uname -r) --enablerepo=Plus-debuginfo --enablerepo=Plus -y

2、创立长期工作目录,下载零碎内核的 SRPM 包:

# mkdir /tmp/work # uname -r 4.19.91-25.2.an7.x86_64 # cd /tmp/work # wget https://mirrors.openanolis.cn/anolis/7.9/Plus/source/Packages/kernel-4.19.91-25.2.an7.src.rpm

3、启动并进入容器:

# podman run -itd --name=plugsched -v /tmp/work:/tmp/work -v /usr/src/kernels:/usr/src/kernels -v /usr/lib/debug/lib/modules:/usr/lib/debug/lib/modules docker.io/plugsched/plugsched-sdk # podman exec -it plugsched bash # cd /tmp/work

4、提取 4.19.91-25.1.al7.x86_64 内核源码:

# plugsched-cli extract_src kernel-4.19.91-25.2.an7.src.rpm ./kernel

5、进行边界划分与提取:

# plugsched-cli init 4.19.91-25.2.an7.x86_64 ./kernel ./scheduler

6、提取后的调度器模块代码在 ./scheduler/kernel/sched/mod 中,简略批改 __schedule 函数,而后编译打包成调度器 rpm 包:

diff --git a/kernel/sched/mod/core.c b/kernel/sched/mod/core.cindex f337607..88fe861 100644--- a/kernel/sched/mod/core.c+++ b/kernel/sched/mod/core.c@@ -3235,6 +3235,8 @@ static void __sched notrace __schedule(bool preempt)   struct rq *rq;   int cpu; +  printk_once("scheduler: Hi, I am the new scheduler!\n");+   cpu = smp_processor_id();   rq = cpu_rq(cpu);   prev = rq->curr;
# plugsched-cli build /tmp/work/scheduler

7、将生成的 rpm 包拷贝到宿主机,退出容器,并装置调度器包,调度器日志显示新批改的调度器曾经失效:

# cp /usr/local/lib/plugsched/rpmbuild/RPMS/x86_64/scheduler-xxx-4.19.91-25.2.an7.yyy.x86_64.rpm /tmp/work# exitexit# rpm -ivh /tmp/work/scheduler-xxx-4.19.91-25.2.an7.yyy.x86_64.rpm# dmesg | tail -n 10[878.915006] scheduler: total initialization time is        5780743 ns[878.915006] scheduler module is loading[878.915232] scheduler: Hi, I am the new scheduler![878.915232] scheduler: Hi, I am the new scheduler![878.915990] scheduler load: current cpu number is               64[878.915990] scheduler load: current thread number is           626[878.915991] scheduler load: stop machine time is            243138 ns[878.915991] scheduler load: stop handler time is            148542 ns[878.915992] scheduler load: stack check time is              86532 ns[878.915992] scheduler load: all the time is                 982076 ns

咱们通过以上晓得了 Plugsched 是什么、利用案例,那它实现原理是什么?

调度器子系统在内核中并非是一个独立的模块,而是内嵌在内核中,与内核其它局部严密相连。Plugsched 采纳“模块化”的思维:它提供了边界划分程序,确定调度器子系统的边界,把调度器从内核代码中提取到独立的目录中,开发人员可对提取出的调度器代码进行批改,而后编译成新的调度器内核模块,动静替换内核中旧的调度器。对子系统进行边界划分和代码提取,须要处理函数和数据,而后生成一个独立的模块。

对于函数而言,调度器模块对外出现了一些要害的函数,以这些函数为入口就能够进入模块中,咱们称之为接口函数。通过替换内核中的这些接口函数,内核就能够绕过原有的执行逻辑进入新的调度器模块中执行,即可实现函数的降级。模块中的函数,除了接口函数外,还有外部函数,其它的则是内部函数。

对于数据,调度器模块默认应用并继承内核中原有的数据,对于调度器重要的数据,比方运行队列状态等,能够通过状态重建技术主动从新初始化,这类数据属于公有数据,而其它则是共享数据。为了灵活性,plugsched 容许用户手动设置公有数据,手动设置的公有数据会在模块中保留定义,但须要对它们进行初始化。对于构造体而言,plugsched 将只被调度器拜访的构造体成员分类为外部成员,其它为非外部成员。调度器模块容许批改外部成员的语义,禁止批改非外部成员的语义。如果构造体所有成员都是外部成员,则调度器模块容许批改整个构造体。然而,倡议优先应用构造体中的保留字段,而不是批改现有成员。

设计方案

Plugsched 次要蕴含两大部分,第一局部是调度器模块边界划分与代码提取局部,第二局部是调度器模块热降级局部,这两局部是整个计划的外围。其整体设计方案如下:


图 2 plugsched 整体架构

首先进行的是调度器模块边界划分和代码提取流程,因为调度器自身并不是模块,因而须要明确调度器的边界能力将它模块化。边界划分程序会依据边界配置信息(次要蕴含代码文件、接口函数等信息)从内核源代码中将调度器模块的代码提取到指定目录,而后开发人员可在此基础上进行调度器模块的开发,最初编译生成调度器 RPM 包,并可装置在对应内核版本的零碎中。装置后会替换掉内核中原有的调度器,装置过程会经验以下几个要害过程:

  • 符号重定位:解析模块对局部内核符号的拜访
  • 栈安全检查:相似于 kpatch,函数替换前必须进行栈安全检查,否则会呈现宕机的危险。plugsched 对栈安全检查进行了并行和二分优化,晋升了栈安全检查的效率,升高了停机工夫
  • 接口函数替换:用模块中的接口函数动静替换内核中的函数
  • 调度器状态重建:采纳通用计划主动同步新旧调度器的状态,极大的简化数据状态的一致性保护工作

总结:基于以上介绍,整体来看,Plugsched 使得调度器从内核中解放出来,开发人员能够对调度器进行专项定制,而不局限于内核通用调度器;内核保护也变得更加轻松,因为开发人员只须要关注通用调度器的开发与迭代,定制化调度器可通过 RPM 包的模式进行公布;内核调度器代码也会变得简洁,无需再被各个场景的优化混同起来。

将来,plugsched 会反对新版本内核和其它平台,继续对其易用性进行优化,并提供更多的利用案例。最初,欢送更多的开发者能参加到 plugsched 中。

原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0