进程调度3「CFS-Completely Fair Scheduling」

CFS是内核使用的一种调度器或调度类,它主要负责处理三种调度策略:SCHED_NORMAL、SCHED_BATCH和SCHED_IDLE。从上一篇文章 可知,调度器的核心在挑选下一个运行的进程时有可能会遍历所有的调度类别。实际上系统大多数进程通常都是CFS调度类负责处理的,因此为了优化下一个进程的挑选调度器核心会先判断当前进程是否采用了CFS调度策略,若是,则直接调用CFS代码来挑选下一个进程,若不是或CFS代码未能挑选到一个合适的进程,则会调用各个调度类的挑选函数来寻找一个合适的进程。若CFS代码寻找到了合适的下一个运行的进程,则直接返回该进程的实例而不会再遍历调度类。本文将主要关注下列的CFS活动和行为:

  • 将一个任务插入运行队列
  • 从运行队列中挑选一个合适的任务
  • 从运行队列中移除一个任务
  • 处理周期滴答「periodic ticks」
  • 管理调度实体「scheduling entities」的运行时间
  • 管理时间片「time slices」

  • Target Platform: Rock960c
  • **ARCH:**arm64
  • **Linux Kernel:**linux-4.19.27

将一个任务插入运行队列

enqueue_task_fair_codeflow.svg

图1 enqueue_task_fair函数的调用路径

enqueue_task_fair():将一个任务插入CFS运行队列

enqueue_entity():更新调度实体的vruntime字段并将它插入红黑树
place_entity():计算调度实体的vruntime
__enqueue_entity():将调度实体插入红黑树

从运行队列中挑选一个任务

pick_next_task_fair_codeflow.svg

图2 pick_next_task_fair函数的调用路径

pick_next_task():寻找将要运行的下一个任务

pick_next_task_fair():从CFS运行队列中寻找下一个任务
pick_next_entity():在红黑树上寻找下一个即将运行的调度实体
put_prev_entity():删除当前的调度实体
set_next_entity():将以参数形式传入的调度实体设置成当前调度实体

从运行队列中移除一个任务

dequeue_task_fair_codeflow.svg

图3 dequeue_task_fair函数的调用路径

dequeue_task_fair():从CFS运行队列中移除一个任务

dequeue_entity():从红黑树中移除一个调度实体

周期滴答「periodic ticks」的处理

task_tick_fair():尝试抢占当前的任务

entity_tick():更新CFS运行队列的调度信息以及尝试抢占当前调度实体
check_preempt_tick():尝试抢占当前调度实体

调度实体的runtime管理

update_curr_codeflow.svg

图3 update_curr函数的调用路径

update_curr():更新CFS运行队列和调度实体的runtime相关字段

calc_delta_fair():根据NICE_0_LOAD和se->load加权计算更新delta
__calc_delta():根据load_weight和delta_exec计算权重「weight」

时间片「time slice」管理

sched_slice():计算调度实体的时间片「time slice」

__sched_period():根据任务数量计算调度延迟「scheduling latency」

sched_vslice():依据调度实体的负载权重计算其虚拟时间片「virtual time slice」

update_sysctl():初始化时间片相关的变量

update_sysctl_codeflow.svg

图3 update_sysctl函数的调用路径