调度实体节点入队逻辑(进入任务就绪队列):
// 比较两个调度实体的 vruntime 大小staticinlineintentity_before(struct sched_entity *a, struct sched_entity *b){return (s64)(a->vruntime - b->vruntime) < 0;}// 将调度实体按 vruntime 插入红黑树staticvoid __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) {structrb_node **link = &cfs_rq->tasks_timeline.rb_root.rb_node;structrb_node *parent = NULL;structsched_entity *entry;bool leftmost = true; // 标记是否将成为最左节点// 从根节点遍历红黑树,寻找合适的插入位置while (*link) { parent = *link;// 从 rb_node 获取包含它的 sched_entity entry = rb_entry(parent, struct sched_entity, run_node);if (entity_before(se, entry)) { link = &parent->rb_left; // vruntime 更小,走左子树 } else { link = &parent->rb_right; // vruntime 更大或相等,走右子树 leftmost = false; } }// 将新节点链接到父节点下 rb_link_node(&se->run_node, parent, link);// 调整红黑树平衡,并更新缓存的最左节点 rb_insert_color_cached(&se->run_node, &cfs_rq->tasks_timeline, leftmost);}
2.3. vruntime 更新
update_curr 负责更新当前运行任务的 vruntime,在任何可能影响运行时间计算的时刻被调用。
staticvoidupdate_curr(struct cfs_rq *cfs_rq){structsched_entity *curr = cfs_rq->curr;// 获取任务时钟(不含中断时间) u64 now = rq_clock_task(rq_of(cfs_rq)); u64 delta_exec; ... delta_exec = now - curr->exec_start; ... curr->exec_start = now; ...// 根据权重计算加权虚拟时间:权重越大 → vruntime 增长越慢 curr->vruntime += calc_delta_fair(delta_exec, curr); ...}
calc_delta_fair 根据任务权重调整 vruntime 增量,内部调用 __calc_delta 完成计算。
staticinline u64 calc_delta_fair(u64 delta, struct sched_entity *se){if (unlikely(se->load.weight != NICE_0_LOAD)) { delta = __calc_delta(delta, NICE_0_LOAD, &se->load); }return delta;}
2.4. 进程任务调度
调度函数(__schedule)从当前 CPU 的任务就绪队列 cfs_rq 中取出一个合适的任务进行调度。
__schedule -> cur cpu -> rq -> cfs_rq-> pick_next_task -> fair_sched_class -> pick_next_task_fair -> context_switch(rq, prev, next)
核心调度函数 __schedule:
// 核心调度函数:选择下一个任务并执行上下文切换staticvoid __sched notrace __schedule(bool preempt) {structtask_struct *prev, *next;structrq *rq;int cpu; cpu = smp_processor_id(); // 获取当前 CPU ID rq = cpu_rq(cpu); // 获取当前 CPU 的运行队列 prev = rq->curr; // 当前正在运行的任务 ...// 从运行队列中选择下一个要运行的任务 next = pick_next_task(rq, prev, &rf); ...if (likely(prev != next)) { ... rq->curr = next; ...// 保存 prev 上下文,加载 next 上下文 rq = context_switch(rq, prev, next, &rf); } ...}
创建任务时根据优先级绑定调度器:
intsched_fork(unsignedlong clone_flags, struct task_struct *p){ ...if (dl_prio(p->prio)) {return -EAGAIN; } elseif (rt_prio(p->prio)) { p->sched_class = &rt_sched_class; } else {// 普通优先级进程使用完全公平调度器调度 p->sched_class = &fair_sched_class; } ...}
选择下一个运行任务(优先走 CFS 快速路径,否则遍历所有调度类):
staticinline struct task_struct *pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf){conststructsched_class *class;structtask_struct *p;// 快速路径:所有任务都在 CFS 中,直接选择if (likely((prev->sched_class == &idle_sched_class || prev->sched_class == &fair_sched_class) && rq->nr_running == rq->cfs.h_nr_running)) { p = fair_sched_class.pick_next_task(rq, prev, rf);if (unlikely(p == RETRY_TASK))goto again;if (unlikely(!p)) p = idle_sched_class.pick_next_task(rq, prev, rf);return p; }again:// 按优先级从高到低遍历所有调度类 for_each_class(class) { p = class->pick_next_task(rq, prev, rf);if (p) {if (unlikely(p == RETRY_TASK))goto again;return p; } } ...}
CFS 选择 vruntime 最小的调度实体作为下一个任务:
static struct task_struct *pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf){structcfs_rq *cfs_rq = &rq->cfs;structsched_entity *se;structtask_struct *p; ...structsched_entity *curr = cfs_rq->curr; ...// 从红黑树中选择 vruntime 最小的调度实体 se = pick_next_entity(cfs_rq, curr);// 从调度实体获取对应的 task_struct p = task_of(se); ...// 将前一个任务放回运行队列 put_prev_entity(cfs_rq, &prev->se);// 将选中的任务从队列取出,准备运行 set_next_entity(cfs_rq, se); ...return p;}
3. 核心结构
为了进一步理解 CFS 的调度源码,下面简单地梳理一下相关的数据结构关系。
3.1. 调度结构关系
| |
|---|
| 每 CPU 运行队列,管理该 CPU 上所有可运行任务,包含 cfs_rq、rt_rq 等子队列。 |
| CFS 运行队列,用红黑树按 vruntime 排序调度实体,最左节点即下一个调度目标。 |
| 进程/线程核心结构,包含 PID、优先级、状态、调度实体等信息。 |
| 调度实体,调度器的基本操作单元,存储 vruntime、权重等调度信息。 |
| 调度类接口,定义 enqueue_task、pick_next_task 等操作,不同调度策略各自实现。 |
3.2. 结构描述
3.2.1. 运行队列 rq
// 每 CPU 一个运行队列,缓存行对齐DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);structrq {unsignedint nr_running; // 可运行任务总数structcfs_rqcfs;// CFS 运行队列structtask_struct *curr;// 当前运行任务 u64 clock_task; // 任务时钟(不含中断时间)};
3.2.2. 完全公平调度运行队列 cfs_rq
structload_weight {unsignedlong weight; // 权重越大,vruntime 增长越慢 ...};structcfs_rq {structload_weightload;unsignedlong runnable_weight; // 队列总权重,用于负载均衡unsignedint nr_running; // 可运行任务数unsignedint h_nr_running; // 层级任务计数(CGroup) u64 exec_clock; // 累计执行时间 u64 min_vruntime; // 队列最小 vruntimestructrb_root_cachedtasks_timeline;// 红黑树structsched_entity *curr;structsched_entity *next;structrq *rq; ...};// 缓存最左节点,O(1) 获取最小 vruntimestructrb_root_cached {structrb_rootrb_root;structrb_node *rb_leftmost;};
3.2.3. 进程结构 task_struct
structtask_struct {structthread_infothread_info;volatilelong state; // TASK_RUNNING, TASK_INTERRUPTIBLE 等 ...conststructsched_class* sched_class;// 调度类structsched_entityse;// 调度实体int on_rq; // 是否在运行队列上int prio; // 动态优先级 ...};
3.2.4. 调度实体 sched_entity
调度实体可以是一个进程或一个进程组,调度器依据其信息决定 CPU 时间分配。
structload_weight {unsignedlong weight; // 负载权重 u32 inv_weight; // weight 的倒数,避免除法运算};structsched_entity {structload_weightload;unsignedlong runnable_weight;structrb_noderun_node;// 红黑树节点structlist_headgroup_node;unsignedint on_rq; // 是否在就绪队列上 u64 exec_start; // 时间片开始时间 u64 sum_exec_runtime; // 实际 CPU 占用时间 u64 vruntime; // 虚拟运行时间 ...};
3.2.5. 调度器 sched_class
structsched_class {conststructsched_class *next;// 调度类链表void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);structtask_struct * (*pick_next_task)(structrq *rq, structtask_struct *prev, structrq_flags *rf);void (*put_prev_task)(struct rq *rq, struct task_struct *p); ...};
内核调度类实例(优先级从高到低):
externconststructsched_classstop_sched_class;// 停止调度(关机等)externconststructsched_classdl_sched_class;// 截止期调度(硬实时)externconststructsched_classrt_sched_class;// 实时调度externconststructsched_classfair_sched_class;// 完全公平调度(默认)externconststructsched_classidle_sched_class;// 空闲调度
4. 参考
- linux 调度子系统 8 - schedule 函数
- 处理器调度 (RR, MLFQ 和 CFS;优先级翻转;多处理器调度)