Linux|龙蜥开源Plugsched:首次实现 Linux kernel 调度器热升级( 三 )


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@@ -32356 +32358 @@ static void __sched notrace __schedule(bool preempt) struct rq *rq; int cpu; + printk_once(\"scheduler: Hi I am the new scheduler!\\");+ 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 包 , 并可安装在对应内核版本的系统中 。 安装后会替换掉内核中原有的调度器 , 安装过程会经历以下几个关键过程: