如何编写并替换Linux调度器?内核关联文件与实现方法咨询
嘿,我来帮你理清楚这两类调度器的区别以及自定义实现的路径——你提到的/sys/block/hda/queue/scheduler确实是I/O调度器的范畴,但进程调度器是内核更核心的部分,咱们分开说:
一、两类调度器的核心关联文件位置
I/O调度器相关文件
I/O调度器是针对磁盘等块设备的I/O请求做调度的,内核源码里的核心位置如下:
- 每个调度器的实现文件都在
block/iosched/目录下,比如noop调度器对应noop-iosched.c,cfq对应cfq-iosched.c,deadline对应deadline-iosched.c - 调度器的注册、管理逻辑在
block/elevator.c里,这里负责把不同的I/O调度器纳入系统管理,也是你修改后能在/sys/block/*/queue/scheduler列表里看到新调度器的关键 - 配置和编译相关的文件:
block/Kconfig(添加调度器的编译选项)、block/Makefile(指定编译规则)
进程调度器相关文件
进程调度器负责决定CPU上运行哪个进程,是内核最核心的组件之一,核心文件都在kernel/sched/目录下:
sched.h:定义了调度器的核心数据结构,比如进程描述符struct task_struct、调度类struct sched_class(每个调度器都要实现这个结构体)sched.c:实现了调度的核心入口逻辑,比如调度触发函数schedule()- 各个内置调度器的实现文件:CFS(完全公平调度器)在
fair.c,实时调度器在rt.c,deadline进程调度器在deadline.c - 调度类的链表管理也在
sched.c里,所有调度器通过这个链表参与调度决策
二、自定义调度器的实现步骤
自定义I/O调度器(相对简单,可做成模块)
- 编写调度器代码:在
block/iosched/下新建你的调度器文件(比如my-iosched.c),参考现有调度器的结构,实现核心函数:初始化I/O队列、添加I/O请求、挑选下一个要处理的请求等 - 添加编译配置:
- 修改
block/Kconfig,添加你的调度器选项:config IOSCHED_MY tristate "My Custom I/O Scheduler" default m help This is my own I/O scheduler for testing. - 修改
block/Makefile,增加编译规则:obj-$(CONFIG_IOSCHED_MY) += my-iosched.o
- 修改
- 编译测试:重新编译内核或单独编译模块,加载模块后,就能在
/sys/block/hda/queue/scheduler里看到你的调度器名称,用echo my > /sys/block/hda/queue/scheduler即可切换使用
自定义进程调度器(内核核心部分,需编译进内核)
- 编写调度器逻辑:在
kernel/sched/下新建你的调度器文件(比如my_sched.c),定义自己的调度类struct sched_class my_sched_class,实现必要的回调函数:enqueue_task:把进程加入调度队列dequeue_task:把进程从调度队列移除pick_next_task:选择下一个要运行的进程put_prev_task:处理当前进程切换出CPU的逻辑- 可选实现
task_tick(时间片到期时的处理)等函数
- 关联到内核调度体系:
- 修改
kernel/sched/sched.h,添加你的调度类声明 - 修改
kernel/sched/sched.c,在init_sched_classes()函数里把你的调度类加入调度类链表(位置决定了调度优先级,比如放在CFS之前或之后)
- 修改
- 编译测试:重新编译内核,启动系统后,可以通过修改进程的调度策略(比如用
chrt命令)来让进程使用你的自定义调度器
一些额外提醒
- 不管是哪种调度器,先仔细研究现有内置调度器的实现逻辑,照着参考会少踩很多坑
- 内核开发要特别注意并发、内存安全问题,尤其是进程调度器,涉及到全局运行队列的操作,调试要谨慎
- I/O调度器做成模块的话,调试和修改会更灵活;进程调度器一般需要编译进内核,因为它是系统启动时就需要的核心组件
内容的提问来源于stack exchange,提问作者TaHoC
相关产品推荐
相关产品推荐

