调度器类sched_class
内核版本:linux 6.15.7按优先级,从高到低:STOP>RT>FAIR>EXT>IDLE二、调度器类存在哪调度器类按照优先级存在__sched_class_highest开始、以__sched_class_lowest结束的段中。每个调度器类存在在对应的xx_sched_class段中。链接脚本vmlinux.lds.h控制了各个调度器类在段中的位置顺序,这个顺序决定了优先级关系。__se
内核版本:linux 6.15.7
一、sched_class种类
按优先级,从高到低:STOP > RT > FAIR(cfs) > EXT(sched_ext) > IDLE
[root@hadoop02 linux-6.15.7]# grep -nr "DEFINE_SCHED_CLASS"
kernel/sched/ext.c:4121:DEFINE_SCHED_CLASS(ext) = {
kernel/sched/deadline.c:3140:DEFINE_SCHED_CLASS(dl) = {
kernel/sched/rt.c:2613:DEFINE_SCHED_CLASS(rt) = {
kernel/sched/fair.c:13627:DEFINE_SCHED_CLASS(fair) = {
kernel/sched/idle.c:516:DEFINE_SCHED_CLASS(idle) = {
kernel/sched/stop_task.c:97:DEFINE_SCHED_CLASS(stop) = {
二、sched_class作用
sched_class(Scheduling Class)描述不同类别任务的调度逻辑,比如实时任务(RT),normal任务(FAIR等),struct sched_class提供一个通用模板,具体的调度器类需要实现其中的函数。
struct sched_class {
#ifdef CONFIG_UCLAMP_TASK
int uclamp_enabled;
#endif
void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
bool (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
void (*yield_task) (struct rq *rq);
bool (*yield_to_task)(struct rq *rq, struct task_struct *p);
void (*wakeup_preempt)(struct rq *rq, struct task_struct *p, int flags);
……
三、sched_class的优先级
调度器类按照优先级存在__sched_class_highest开始、以__sched_class_lowest结束的段中。
每个调度器类存在对应的xx_sched_class段中。
链接脚本vmlinux.lds.h控制了各个调度器类在段中的位置顺序,这个顺序决定了优先级关系。
__section("__" #name "_sched_class")定义了各调度器类存放的段名xx_sched_class。
kernel/sched/sched.h
#define DEFINE_SCHED_CLASS(name) \
const struct sched_class name##_sched_class \
__aligned(__alignof__(struct sched_class)) \
__section("__" #name "_sched_class")
__sched_class_highest和__sched_class_lowest两个变量是在vmlinux.lds.h中定义的,调度器类按照优先级存在__sched_class_highest开始的段中。
kernel/sched/sched.c
/* Defined in include/asm-generic/vmlinux.lds.h */
extern struct sched_class __sched_class_highest[];
extern struct sched_class __sched_class_lowest[];
vmlinux.lds.h中决定了这些调度器类在段中的存放位置顺序。
include/asm-generic/vmlinux.lds.h
/*
* The order of the sched class addresses are important, as they are
* used to determine the order of the priority of each sched class in
* relation to each other.
*/
#define SCHED_DATA \
STRUCT_ALIGN(); \
__sched_class_highest = .; \
*(__stop_sched_class) \
*(__dl_sched_class) \
*(__rt_sched_class) \
*(__fair_sched_class) \
*(__ext_sched_class) \
*(__idle_sched_class) \
__sched_class_lowest = .;
仅通过调整vmlinux.lds.h文件中sched_class的顺序来修改调度类优先级是不可行的,因为内核中多处源码与该顺序存在强耦合关系。若强行调整顺序,可能导致遍历调度类时出现死循环问题,具体表现为系统启动时卡在"Loading initial ramdisk"阶段。以next_active_class为例说明(内核中还有很多其他依赖sched_class的顺序的代码)
static inline const struct sched_class *next_active_class(const struct sched_class *class)
{
class++;
#ifdef CONFIG_SCHED_CLASS_EXT
if (scx_switched_all() && class == &fair_sched_class)
class++;
if (!scx_enabled() && class == &ext_sched_class)
class++;
#endif
return class;
}
假设将fair_sched_class与 ext_sched_class 交换顺序、scx_enabled() 为 false):
原始顺序(正常):
... -> fair_sched_class -> ext_sched_class -> idle_sched_class
遍历逻辑:
- 进入 fair_sched_class
- 调用 next_active_class(fair_sched_class) → 得到 ext_sched_class
- 检查 !scx_enabled() → true,所以跳过 ext → 返回 idle_sched_class
改动后的顺序:
... -> ext_sched_class -> fair_sched_class -> idle_sched_class
遍历逻辑:
- 进入 ext_sched_class
- 调用 next_active_class(ext_sched_class) → 指向 fair_sched_class
- 检查 !scx_enabled() 且 class == &ext_sched_class → false(因为当前是 fair)
- 检查 scx_switched_all() && class == &fair_sched_class → 可能 true 或 false
- 如果 scx_switched_all() 没启用,继续返回 fair;
但此时 fair 在 ext 后面,后续遍历逻辑可能再调回 ext(或者继续访问无效 class),导致:
- 循环遍历(死循环)
- 越界访问
- 调度类指针错乱,造成调度器无法 pick 任务
内核挂死在 early boot 阶段(通常表现为卡在 initramfs,CPU 忙等待)。
四、sched_class的遍历操作
kernel/sched/sched.h
#define for_each_class(class) \
for_class_range(class, __sched_class_highest, __sched_class_lowest)
一个 task在任意时刻只能属于一个 sched_class,由struct task_struct->sched_class决定:
struct task_struct {
const struct sched_class *sched_class;
五、sched_class的函数集
以struct sched_class为模板,各个调度器类需要实现部分或全部函数接口,在代码中可以搜索关键字"DEFINE_SCHED_CLASS",以sched_ext调度器类为例:
DEFINE_SCHED_CLASS(ext) = {
.enqueue_task = enqueue_task_scx,
.dequeue_task = dequeue_task_scx,
.yield_task = yield_task_scx,
.yield_to_task = yield_to_task_scx,
.wakeup_preempt = wakeup_preempt_scx,
.balance = balance_scx,
.pick_task = pick_task_scx,
.put_prev_task = put_prev_task_scx,
.set_next_task = set_next_task_scx,
#ifdef CONFIG_SMP
.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,
#endif
.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,
#ifdef CONFIG_UCLAMP_TASK
.uclamp_enabled = 1,
#endif
}
更多推荐


所有评论(0)