CFS 的调度类型:普通调度 vs 组调度
在Linux的完全公平调度器(CFS)中,普通调度(默认模式)和组调度(Group Scheduling)是两种互补的任务组织方式,主要区别在于资源分配的层次结构和公平性控制的范围。以下是它们的对比与实现细节:
1. 普通调度(默认CFS)
- 核心思想:以**单个任务(task)**为调度单位,所有任务在统一的公平队列(
cfs_rq)中竞争CPU时间。 - 关键特性:
- 公平性目标:通过虚拟运行时(
vruntime)保证每个任务获得平等的CPU时间比例(权重调整除外)。 - 权重(
nice值):通过task_struct->se.load权重区分任务优先级(nice -20到19)。 - 调度单位:每个任务独立计算
vruntime,红黑树中选择最小vruntime的任务运行。
- 公平性目标:通过虚拟运行时(
- 局限性:
- 若某用户或进程组启动大量任务,会挤占其他用户/组的资源(如用户A运行100个进程,用户B运行1个进程,B可能被“饿死”)。
2. 组调度(CFS Group Scheduling)
- 核心思想:引入**任务组(
task_group)**作为调度单位,实现层级公平性(Hierarchical Fair Scheduling)。 - 关键特性:
- 层级化调度实体:
- 每个任务组拥有自己的
cfs_rq和调度实体(sched_entity),组内任务共享组的vruntime。 - 组之间按权重分配CPU,组内再按任务权重分配(双层公平)。
- 每个任务组拥有自己的
- 控制组(cgroups)集成:通过
cpu控制器(如cpu.shares)动态调整组权重。 - 资源隔离:防止单个用户或容器垄断CPU。
- 层级化调度实体:
- 实现示例:
# 创建CPU控制组并设置权重 mkdir /sys/fs/cgroup/cpu/groupA echo 512 > /sys/fs/cgroup/cpu/groupA/cpu.shares # 默认1024,groupA获得50% CPU - 优势场景:
- 多用户系统(如云服务器)中保障用户间公平性。
- 容器化环境(Docker/Kubernetes)中限制容器的CPU配额。
3. 对比总结
| 特性 | 普通调度 | 组调度 |
|---|---|---|
| 调度单位 | 单个任务(task_struct) | 任务组(task_group) |
| 公平性层次 | 全局任务间公平 | 组间公平 + 组内任务公平 |
| 资源控制 | 仅通过nice调整任务权重 | 通过cgroups控制组权重(cpu.shares) |
| 适用场景 | 单用户桌面环境 | 多租户服务器、容器集群 |
| 红黑树组织 | 全局一棵cfs_rq红黑树 | 每个组维护独立的cfs_rq树 |
4. 底层实现关键点
-
组调度的数据结构:
task_group结构体包含组的cfs_rq和se(调度实体)。- 任务组的
se会被加入父组的cfs_rq,形成层级树。
// 内核结构示例(简化) struct task_group {struct cfs_rq cfs_rq; // 组自己的CFS运行队列struct sched_entity se; // 组作为调度实体unsigned long shares; // 权重(通过cpu.shares设置) }; -
调度决策流程:
- 从根
cfs_rq开始,递归选择vruntime最小的组或任务。 - 组间选择依赖组的
se.vruntime,组内选择任务的se.vruntime。
- 从根
-
权重分配公式:
- 组间CPU比例 =
group.shares / parent.shares_sum。 - 组内任务CPU比例 =
task.load / group.load_sum。
- 组间CPU比例 =
5. 性能与调试
- 开销:组调度因层级管理会增加少量开销(尤其在深度嵌套的cgroups时)。
- 调试工具:
cat /proc/sched_debug:查看所有cfs_rq和se的vruntime。systemd-cgtop:实时监控cgroups的CPU使用率。
通过组调度,CFS实现了从单任务公平到多级资源控制的扩展,成为现代Linux系统资源管理的基石。
