日期 | 内核版本 | CPU架构 | 作者 |
2019.04.06 | Linux-5.0 | PowerPC | LoneHugo |
系列文章:https://blog.csdn.net/Vince_/article/details/89054330
1. schedule()接口
首先需要关闭抢占,防止调度重入,然后调用__schedule,进行current相关的处理,比如有待决信号,则继续标记状态为TASK_RUNNING,或者如果需要睡眠则调用deactivate_task将从运行队列移除后加入对应的等待队列,通过pick_next_task选择下一个需要执行的进程,进行context_switch进入新进程运行。
2. pick_next_task
首先判断当前进程调度类sched_class是否为fair_sched_calss,也就是CFS,如果是且当前cpu的调度队列下所有调度实体数目与其下面所有CFS调度类的下属群组内的调度实体数目总数相同,即无RT等其他调度类中有调度实体存在(rq->nr_running == rq->cfs.h_nr_running),则直接返回当前调度类fair_sched_class的pick_next_task选择结果,否则需要遍历所有调度类for_each_class(class),返回class->pick_next_task的非空结果。
这里需要关注的是for_each_class的遍历过程,从sched_class_highest开始,也就是stop_sched_class。
#define sched_class_highest (&stop_sched_class)
#define for_each_class(class) \
for (class = sched_class_highest; class; class = class->next)
extern const struct sched_class stop_sched_class;
extern const struct sched_class dl_sched_class;
extern const struct sched_class rt_sched_class;
extern const struct sched_class fair_sched_class;
extern const struct sched_class idle_sched_class;
3. 各个调度类的关联
按照优先级依次罗列组成单链表:
stop_sched_class->next->dl_sched_class->next->rt_sched_class->next->fair_sched_class->next->idle_sched_class->next=MULL
4. 调度类的注册
在编译过程中通过early_initcall(cpu_stop_init)进行stop相关的注册,cpu_stop_init对cpu_stop_threads进行了注册,其create方法被调用时实际执行了cpu_stop_create->sched_set_stop_task,对stop的sched_class进行注册,create的执行路径如下:
cpu_stop_init->
smpboot_register_percpu_thread->
smpboot_register_percpu_thread_cpumask->
__smpboot_create_thread->
cpu_stop_threads.create(即cpu_stop_create)
现在回到pick_next_task,由于stop_sched_class作为最高级别调度类将所有系统中的调度类链接起来,遍历过程查看所有sched_class,从最高优先级开始,直到找到一个可以调度的进程返回,如果整个系统空闲,则之中会调度到系统初始化进程init_task,其最后被设置为idle进程在系统空闲时的调度执行,上面对sched_init的解释里面有详细说明。