[内核源码] 浅析 CFS 完全公平调度器工作原理

2025-10-10

本文结合 Linux 5.0.1 内核源码,浅析 Linux 进程管理中的完全公平调度器(CFS)工作原理。


1. 概述

完全公平调度器(Completely Fair Scheduler, CFS)自 Linux 内核 2.6.23 版本引入,作为替代传统调度器的默认进程调度算法,其设计目标是在所有可运行进程之间近似地实现理想的、无差别的公平处理器时间分配。

在多任务操作系统中,调度器 是内核的核心组件,负责在竞争性的进程之间高效、合理地分配有限的 CPU 时间资源。CFS 的提出,旨在克服先前调度算法(如 O(1) 调度器)在交互性、公平性及可扩展性方面的局限,它通过一种精妙的模型,确保每个进程能获得与其优先级相匹配的、近乎平等的 CPU 时间份额。

CFS 的实现基石是 虚拟运行时间(vruntime)。该指标并非简单的实际运行时间,而是一种经过进程优先级(权重)归一化处理后的度量。CFS 通过持续追踪每个进程的 vruntime,并始终选择 vruntime 最小的进程进行调度,以此在宏观上逼近所有进程 vruntime 同步增长的理想公平状态。

然而,在一个复杂的抢占式多任务环境中,vruntime 虽是调度决策的核心依据,但并非唯一因素。完整的调度行为还需综合考虑诸如处理器亲和性、负载均衡、中断与唤醒事件,以及更复杂的调度策略(如实时进程优先级)等多种因素的动态影响。

源码结构关系,请参考本章 核心结构 部分内容。


2. 虚拟运行时间调度

在 Linux 的完全公平调度器(CFS)设计中,每个 CPU 核心都拥有一个独立的调度域,其就绪队列使用一颗 红黑树 来组织。这棵树的排序关键字是每个进程的 “虚拟运行时间”(vruntime)。

核心机制与 vruntime 的作用:

  1. 排序依据:进程的 vruntime 值决定了它在红黑树中的位置,值越小,位置越靠左。
  2. 调度选择:调度器总是挑选 vruntime 最小的(即红黑树最左侧的)进程投入运行,这保证了最 “饥饿” 的进程能优先获得 CPU。
  3. 公平的实现:进程在 CPU 上运行后,其 vruntime 会随之增长,这使它逐渐向右移动,让位于其他 vruntime 更小的进程。高优先级进程(nice 值小 - 权重大)的 vruntime 增长更慢,因此能更频繁地被调度,从而获得更多的实际 CPU 时间。

2.1. vruntime

vruntime 它表示该进程在 CPU 上实际运行的时间经过加权后的结果。

调度器会优先选择 vruntime 值最小的进程运行,以确保每个进程的 vruntime 尽可能接近,从而实现公平的 CPU 时间分配。

1
新 vruntime = 旧 vruntime + 任务实际运行时间 × (nice 0 的权重 / 当前权重)

从时间计算公式可知:weight 权重越大 → vruntime 涨得越慢 → 更容易抢到 CPU。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 进程任务结构
struct task_struct {
  // 任务调度实体:包含任务在 CFS 调度器中的所有调度相关信息
  struct sched_entity se;
  ...
};

// 负载权重结构:量化任务或调度组在 CPU 资源竞争中的优先级
struct load_weight {
  ...
  // 权重值:决定任务在 CPU 时间分配中的相对比例
  unsigned long weight;
  ...
};

// 调度实体:CFS 调度器管理的基本单位,可以是任务或任务组
struct sched_entity {
  ...
  // 负载权重:该调度实体在 CPU 竞争中的重要程度
  struct load_weight load;
  ...
  // 虚拟运行时间
  u64 vruntime;
  ...
  // 当前任务时间片的开始时间
  u64 exec_start;
  ...
};

2.2. 红黑树管理 vruntime

在 CFS 调度器中,所有可运行任务都作为调度实体按虚拟运行时间(vruntime)组织在红黑树中。
该红黑树通过运行队列的 rq.cfs.tasks_timeline 字段进行管理。

系统在每次调度时,从红黑树中获取最左侧节点(即 vruntime 最小的调度实体)进行调度。
这意味着任务的 vruntime 值越小,其调度优先级越高,越有可能获得 CPU 时间。

下图红黑树最左子树节点数值是 1

图片来源:红黑树维基百科

  • 任务就绪队列(红黑树)和任务调度实体。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// CPU 运行队列
struct rq {
  ...
  // 完全公平调度器
  struct cfs_rq cfs;
  ...
};

// 完全公平调度运行队列(任务就绪队列)
struct cfs_rq {
  // 左端缓存任务节点红黑树
  struct rb_root_cached tasks_timeline;
  ...
};

// 进程任务结构
struct task_struct {
  // 任务调度实体
  struct sched_entity se;
  ...
};

// 左端缓存红黑树
struct rb_root_cached {
  // 根节点
  struct rb_root rb_root;
  // 缓存红黑树最左节点(优化查找 leftmost 节点算法)
  struct rb_node *rb_leftmost;
};

// 任务调度实体:CFS 调度器管理的基本单位,可以是任务或任务组
struct sched_entity {
  ...
  // 进程任务红黑树节点
  struct rb_node run_node;
  ...
  // 虚拟运行时间
  u64 vruntime;
  ...
};
  • 调度实体节点入队逻辑(进入任务就绪队列)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 比较两个调度实体的 vruntime 大小
static inline int entity_before(struct sched_entity *a, struct sched_entity *b) {
    return (s64)(a->vruntime - b->vruntime) < 0;
}

// 将调度实体插入 CFS 运行队列的红黑树中
static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) {
    // 从红黑树根节点开始遍历,link指向当前检查的节点指针位置
    struct rb_node **link = &cfs_rq->tasks_timeline.rb_root.rb_node;
    struct rb_node *parent = NULL;
    struct sched_entity *entry;
    // 标记新节点是否将成为最左节点(vruntime 最小)
    bool leftmost = true;

    // 在红黑树中寻找合适的插入位置
    while (*link) {
        parent = *link;
        // 通过 rb_entry 宏从 rb_node 获取包含它的 sched_entity 结构
        entry = rb_entry(parent, struct sched_entity, run_node);
        
        // 比较新实体与当前节点的 vruntime 大小
        // 注意:不处理键值冲突,相同 vruntime 的节点放在右子树
        if (entity_before(se, entry)) {
            // 新实体 vruntime 更小,向左子树继续查找
            link = &parent->rb_left;
        } else {
            // 新实体 vruntime 更大或相等,向右子树继续查找
            link = &parent->rb_right;
            // 向右查找意味着不会是新的最左节点
            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 统计

  • 更新当前运行任务的 vruntime:update_curr 。系统任何可能影响任务运行时间计算的时刻都会调用 update_curr,确保 vruntime 的实时性和准确性,为公平调度提供可靠的时间基准。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void update_curr(struct cfs_rq *cfs_rq) {
  // 当前运行的调度实体
  struct sched_entity *curr = cfs_rq->curr;
  // 获取当前 CPU 的 clock_task(仅统计任务执行时间,不含中断)。
  // 类似"全局时钟",但仅统计任务执行时间(去除了中断干扰)
  u64 now = rq_clock_task(rq_of(cfs_rq));
  // 任务执行时间增量
  u64 delta_exec;
  ...
  // 计算自上次更新后的执行时间
  delta_exec = now - curr->exec_start;
  ...
  // 更新本次调度的起始时间
  curr->exec_start = now;
  ...
  // 根据任务的权重(weight)计算加权后的虚拟时间
  // 高优先级任务(weight 大) → vruntime 增长慢 → 更容易被调度。
  curr->vruntime += calc_delta_fair(delta_exec, curr);
  ...
}
  • 计算虚拟时间增量:根据任务权重调整虚拟运行时间增量,权重越大,增量越小。该计算过程通过 calc_delta_fair 函数实现,其内部调用 __calc_delta 函数完成具体的权重转换计算。
1
2
3
4
5
6
7
8
9
static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se) {
  // 计算负载权重不为 0 的时间增量
  // NICE_0_LOAD:优先级为 0 时的负载权重
  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)

  • 调度器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// 每 CPU 运行队列管理结构
struct rq {
  ...
  // 完全公平调度器
  struct cfs_rq cfs;
  ...
  // 调度类
  const struct sched_class* sched_class;
  ...
};

// 任务就绪队列 - 完全公平调度器(CFS)运行队列结构
struct cfs_rq {
  // 按 vruntime 排序的任务红黑树,缓存最左节点以优化访问
  struct rb_root_cached tasks_timeline;
  ...
};

// 进程任务结构
struct task_struct {
  // 任务调度实体
  struct sched_entity se;
  ...
};

// 任务调度实体
struct sched_entity {
  ...
  // 进程任务红黑树节点
  struct rb_node run_node;
  ...
};

// 调度类
struct sched_class {
  ...
  // 选择下一个任务的函数指针
  struct task_struct *(*pick_next_task)(
    struct rq *rq, struct task_struct *prev, struct rq_flags *rf);
  ...
};

// 完全公平调度器
const struct sched_class fair_sched_class = {
  ...
  .pick_next_task = pick_next_task_fair,
  ...
};
  • 任务调度逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 核心调度函数:负责选择下一个运行任务并执行上下文切换
static void __sched notrace __schedule(bool preempt) {
  struct task_struct *prev, *next;
  // 当前CPU的运行队列指针
  struct rq *rq;
  int cpu;

  // 获取当前 CPU ID
  cpu = smp_processor_id();
  // 获取当前 CPU 对应的运行队列
  rq = cpu_rq(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);
  }
  ...
}
  • 创建任务,根据优先级设置任务调度器。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 进程/线程创建时根据优先级绑定的调度方式
int sched_fork(unsigned long clone_flags, struct task_struct *p) {
  ...
  if (dl_prio(p->prio)) {
    return -EAGAIN;
  } else if (rt_prio(p->prio)) {
    p->sched_class = &rt_sched_class;
  } else {
    // 普通优先级进程使用完全公平调度器调度
    p->sched_class = &fair_sched_class;
  }
  ...
}
  • 选择合适的调度器,从已选的调度器中获取下一个合适的运行任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
static inline struct task_struct *
pick_next_task(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) {
  const struct sched_class *class;
  struct task_struct *p;

  // 优化:如果所有任务都在公平调度类中,可以直接调用公平调度函数
  // 但前提是前一个任务不是更高调度级别的任务,否则可能失去从其他CPU拉取任务的机会
  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);
    // 如果返回 RETRY_TASK,表示需要重新选择,跳转到 again 标签
    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) {
      // 如果返回 RETRY_TASK,重新开始遍历
      if (unlikely(p == RETRY_TASK))
        goto again;
      return p;
    }
  }
  ...
}
  • 从调度器选择下一个任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// CFS 调度器选择下一个任务的实现
static struct task_struct *
pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf) {
  // 获取 CFS 运行队列
  struct cfs_rq *cfs_rq = &rq->cfs;
  struct sched_entity *se;
  struct task_struct *p;
  ...
  // 当前正在运行的调度实体
  struct sched_entity *curr = cfs_rq->curr;
  ...
  // 从 CFS 红黑树中选择 vruntime 最小的下一个调度实体
  se = pick_next_entity(cfs_rq, curr);
  // 从调度实体转换为任务结构
  p = task_of(se);
  ...
  struct sched_entity *pse = &prev->se;
  // 将当前任务重新放回运行队列(更新状态和位置)
  put_prev_entity(cfs_rq, pse);
  // 将选择的下一个任务从运行队列中取出,准备投入运行
  set_next_entity(cfs_rq, se);
  ...
  return p;
}

3. 核心结构

为了进一步理解 CFS 的调度源码,下面简单地梳理一下相关的数据结构关系。

3.1. 调度结构关系

结构 描述
rq 即通用的运行队列,系统中的每个 CPU 都配备一个对应的 rq 实例。其主要职责是管理该 CPU 上所有处于可运行状态的任务。rq 结构体内部包含了不同调度类的运行队列,像 CFS 运行队列(cfs_rq)、实时运行队列(rt_rq)以及限期调度器运行队列(dl_rq)等。同时,它还保存了一些与调度相关的统计信息,如可运行任务的数量(nr_running)、CPU 负载等,这些信息有助于调度器进行负载均衡和调度决策。
cfs_rq 属于 CFS 调度器的运行队列,CFS 调度器是 Linux 内核默认的普通任务调度器,其设计目标是让每个进程公平地获取 CPU 时间。cfs_rq 运用红黑树来组织可运行的调度实体,红黑树的节点按照调度实体的虚拟运行时间进行排序,虚拟运行时间越小的调度实体越靠近树的左侧。这样,调度器可以高效地找到下一个应该运行的调度实体,实现公平调度。
task_struct Linux 内核里表征进程或线程的核心数据结构,它涵盖了进程全方位的状态信息。具体包括进程的唯一标识符(PID)、调度优先级(用于决定进程获取 CPU 资源的先后顺序)、内存管理信息(如页表、内存映射等)、文件描述符表(记录进程打开的文件)、信号处理信息(进程如何响应各种信号)以及进程的状态(如运行、睡眠、停止等)。这些信息使得内核能够全面管理和控制进程的生命周期和行为。
sched_entity 代表调度实体,是调度器实施调度操作的基本单元。在常规情形下,一个进程对应一个调度实体;但在组调度的场景中,一个进程可能会关联多个调度实体。调度实体存储了与调度相关的关键信息,例如虚拟运行时间(vruntime,在 CFS 调度器中用于衡量进程已使用的 CPU 时间)、调度优先级等。调度器依据这些信息来决定哪个调度实体能够获得 CPU 资源。
sched_class 调度器类的抽象定义,它定义了一套调度器操作的接口,不同的调度策略可以通过实现这些接口来完成特定的调度任务。内核中存在多种调度器类,如 CFS 调度器类、实时调度器类等。sched_class 结构体包含了一系列函数指针,用于实现任务的入队(enqueue_task)、出队(dequeue_task)、选择下一个任务(pick_next_task)、检查抢占(check_preempt_curr)等操作。不同的调度器类通过实现这些函数来体现各自的调度策略,例如实时调度器会优先调度实时任务,而 CFS 调度器则侧重于公平调度普通任务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 运行队列
struct rq {
  ...
  // 完全公平调度器
  struct cfs_rq cfs;
  ...
  // 调度类
  const struct sched_class* sched_class;
  ...
};

// 完全公平调度器运行队列
struct cfs_rq {
  // 红黑树
  struct rb_root_cached tasks_timeline;
  ...
};

// 调度实体
struct sched_entity {
  // 红黑树节点
  struct rb_node run_node;
  ...
};

// 进程/线程结构
struct task_struct {
  ...
  // 调度实体
  struct sched_entity se;
  ...
  // 调度类
  const struct sched_class *sched_class;
  ...
};

// 调度类
struct sched_class {
  ...
  // 任务入队
  void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);
  // 任务出队
  void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);
  ...
};

// 完全公平调度类实例
const struct sched_class fair_sched_class = {
  ...
  .enqueue_task  = enqueue_task_fair,
  .dequeue_task  = dequeue_task_fair,
  ...
};

3.2. 结构描述

3.2.1. 运行队列 rq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 声明每 CPU 运行队列变量,缓存行对齐以避免伪共享
// 每个 CPU 拥有独立的运行队列,消除锁竞争,提升缓存局部性
DECLARE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);

// CPU运行队列管理结构
struct rq {
  // 就绪队列中可运行任务的总数(所有调度类)
  unsigned int nr_running;
  
  // 完全公平调度器(CFS)运行队列,管理普通进程
  struct cfs_rq cfs;
  
  // 当前正在此CPU上运行的任务指针
  struct task_struct *curr;
  
  // 任务时钟:记录 CPU 真正用于执行任务的时间
  // 排除中断处理、空闲时间、管理开销等
  // 更新时机:
  //   - 任务切换时(context_switch)
  //   - 调度器时钟中断时(scheduler_tick)
  u64 clock_task;
};

3.2.2. 完全公平调度运行队列 cfs_rq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 任务或者调度组的负载权重信息
struct load_weight {
  // 负载权重
  // 体现了任务或者调度组在竞争 CPU 资源时的优先级或者重要程度。
  // 权重越大,该任务或者调度组在获取 CPU 时间上就更具优势。
  // 在计算任务的 vruntime 增长速率时,高权重的任务 vruntime 增长得慢
  unsigned long weight;
  ...
};

struct cfs_rq {
  // 负载权重
  struct load_weight load;
  // 队列中所有可运行进程的权重累加结果
  // 内核通过比较不同 CPU 上的 runnable_weight
  // 判断负载是否均衡,决定是否迁移进程。
  unsigned long runnable_weight;
  // 队列中的可运行任务数量
  unsigned int nr_running;
  // 层级调度中的任务计数(支持 CGroup 嵌套)
  unsigned int h_nr_running;
  // cfs_rq 的总任务执行时间
  // 记录该 cfs_rq 中所有任务累计的执行时间(单位:纳秒)
  // 主要用于 负载统计和调度决策(如负载均衡)。
  u64 exec_clock;
  // 当前 cfs_rq 中所有任务的最小虚拟运行时间(vruntime)。
  u64 min_vruntime;
  // 红黑树根节点,按 vruntime 排序所有可运行任务
  struct rb_root_cached tasks_timeline;
  // 当前任务调度实体
  struct sched_entity *curr;
  struct sched_entity *next;
  // cfs_rq 属于哪个 rq 运行队列
  struct rq *rq;
  ...
};

// 红黑树缓存了最左子树的叶子节点,
// 使得获取最左子树叶子节点的时间复杂度为 O(1)
struct rb_root_cached {
  struct rb_root rb_root;
  struct rb_node *rb_leftmost;
};

3.2.3. 进程结构 task_struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 进程状态
#define TASK_RUNNING            0x0000
#define TASK_INTERRUPTIBLE      0x0001
#define TASK_UNINTERRUPTIBLE    0x0002
#define __TASK_STOPPED          0x0004
#define __TASK_TRACED           0x0008
/* Used in tsk->exit_state: */
#define EXIT_DEAD               0x0010
#define EXIT_ZOMBIE             0x0020

// 进程结构
struct task_struct {
  struct thread_info thread_info;
  // 进程当前状态(例如:TASK_RUNNING)
  volatile long state;
  ...
  // 调度类
  const struct sched_class* sched_class;
  // 调度实体
  struct sched_entity se;
  // 任务是否在运行队列上
  int on_rq;
  // 调度器实际使用的动态优先级
  int prio;
  ...
};

// thread information flags
#define TIF_SIGPENDING 2 /* signal pending */
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */

struct thread_info {
  unsigned long flags;    /* low level flags */
  u32           status;   /* thread synchronous flags */
};

3.2.4. 调度实体 sched_entity

调度实体可以是 一个进程或者一个进程组,内核调度器会依据调度实体的相关信息来决定哪个进程可以获得 CPU 时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 任务或者调度组的负载权重信息
struct load_weight {
  // 调度实体的负载权重
  unsigned long weight;
  // 是 weight 的倒数,为提高调度计算效率,
  // 避免在调度计算过程中频繁进行除法运算。
  u32 inv_weight;
};

// 调度实体
struct sched_entity {
  /* For load-balancing: */
  struct load_weight load;
  unsigned long runnable_weight;
  // 红黑树节点
  struct rb_node run_node;
  struct list_head group_node;
  // 任务是否在运行就绪队列上(等待调度)
  unsigned int on_rq;
  // 当前时间片的开始时间
  u64 exec_start;
  // 计算进程/线程的实际CPU占用时间
  u64 sum_exec_runtime;
  // CFS调度的进程/线程虚拟运行时间
  u64 vruntime;
  ...
};

3.2.5. 调度器 sched_class

  • 调度器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct sched_class {
  // 指向另一个调度器类的指针,用于形成一个调度类链表
  const struct sched_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);
  // 当前正在运行的任务主动放弃CPU使用权
  void (*yield_task) (struct rq *rq);
  // 当前正在运行的任务主动让出CPU给指定的任务
  bool (*yield_to_task) (struct rq *rq, struct task_struct *p, bool preempt);
  // 检查是否需要对当前正在运行的任务进行抢占
  void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);
  ...
  // 运行队列中选择下一个要运行的任务
  struct task_struct * (*pick_next_task)(
    struct rq *rq, struct task_struct *prev, struct rq_flags *rf);
  // 处理之前正在运行的任务
  void (*put_prev_task)(struct rq *rq, struct task_struct *p);
  ...
};
  • 完全公平调度器。
1
2
3
4
5
6
7
8
9
10
11
12
// 完全公平调度器
const struct sched_class fair_sched_class = {
  .next                = &idle_sched_class,
  .enqueue_task        = enqueue_task_fair,
  .dequeue_task        = dequeue_task_fair,
  .yield_task          = yield_task_fair,
  .yield_to_task       = yield_to_task_fair,
  .check_preempt_curr  = check_preempt_wakeup,
  .pick_next_task      = pick_next_task_fair,
  .put_prev_task       = put_prev_task_fair,
  ...
};
  • 调度器实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 停止调度
// 通常用于系统关机或紧急情况(如CPU停止调度普通任务)
extern const struct sched_class stop_sched_class;
// 截止期调度:实时调度策略之一,基于截止时间进行任务调度,
// 硬实时任务(Hard Real-Time Task),
// 例如 音视频处理、机器人控制 等,对任务执行时间要求极高
extern const struct sched_class dl_sched_class;
// 实时调度:处理实时优先级任务(Real-Time Priority Tasks),优先级高于普通任务
extern const struct sched_class rt_sched_class;
// 完全公平调度:完全公平调度器,普通任务的主要调度类,负责大多数用户态进程的调度
extern const struct sched_class fair_sched_class;
// 空闲调度:用于CPU空闲时的任务,如果CPU没有任务可运行,就会进入空闲任务
extern const struct sched_class idle_sched_class;

4. 参考