内核版本: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
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐