3. 组调度(Group Scheduling)概念:从单进程调度到任务组调度,CPU时间片的公平分配
在早期的Linux内核中,调度器(O(1)调度器、CFS调度器初期版本)的调度单位是单个进程或线程。这意味着CPU时间片的分配是基于系统中所有独立进程的数量来计算的。这种设计在桌面或通用服务器场景下基本够用,但在多租户、容器化、资源隔离要求高的环境中暴露出了严重的公平性问题。
3.1 单进程调度的局限性
在传统的单进程调度模型中,CFS(Completely Fair Scheduler)维护一个红黑树,每个可运行进程对应一个调度实体(struct sched_entity)。调度器遍历红黑树,选择虚拟运行时间(vruntime)最小的进程来执行。每个进程获得的时间片与其权重(weight)成正比,与系统中总进程数成反比。
单进程调度模型示意图:
单进程调度的不公平场景:
| 用户 | 进程数量 | 每个进程权重 | 总权重 | 获得的CPU时间占比 |
|---|---|---|---|---|
| 用户A | 100 | 1024 | 102400 | 99% |
| 用户B | 1 | 1024 | 1024 | 1% |
3.2 任务组调度(Group Scheduling)的引入
为了解决上述不公平问题,Linux内核从2.6.24版本开始引入了组调度(Group Scheduling)机制,并在后续版本中不断完善。组调度的核心思想是:将进程组织成任务组(Task Group),调度器以任务组为单位分配CPU时间片,组内的进程再按照组内规则进一步分配时间片。
在组调度模型中,调度实体(struct sched_entity)不仅可以代表一个进程,还可以代表一个任务组。每个任务组内部维护自己的红黑树,组内的进程或子组在该红黑树上进行调度。这种层级化的调度结构实现了CPU时间片的多级公平分配。
组调度模型示意图:
3.3 组调度的公平性分析
在组调度模型下,重新审视前面用户A和用户B的场景。假设用户A的100个进程属于任务组A,用户B的1个进程属于任务组B,两个任务组的权重相同(均为1024)。
组调度下的CPU时间分配:
| 任务组 | 组权重 | 组获得的CPU时间 | 组内进程数量 | 每个进程获得的CPU时间 |
|---|---|---|---|---|
| 任务组A(用户A) | 1024 | 50% | 100 | 0.5% |
| 任务组B(用户B) | 1024 | 50% | 1 | 50% |
3.4 组调度的内核实现要点
组调度在内核中的实现涉及以下几个关键数据结构与机制:
struct task_group:内核中表示任务组的主要数据结构。每个任务组包含自己的CFS运行队列(cfs_rq)、调度实体(se)以及用于CPU带宽控制的统计信息。- 层级化调度实体:一个任务组在父调度器的红黑树中表现为一个调度实体(
struct sched_entity),同时该任务组内部又维护自己的红黑树,调度组内的进程或子组。 - 权重继承与计算:任务组的权重可以手动设置(通过cgroup的
cpu.shares),也可以根据组内进程的总权重自动计算。组调度器在分配时间片时,会综合考虑组权重和组内进程的权重。 - 虚拟运行时间(vruntime)的层级维护:每个层级都有自己的
vruntime计算逻辑。任务组在顶层调度器中的vruntime增长速率取决于该组获得的CPU时间比例,而组内进程的vruntime则基于组内分配的时间片进行计算。
组调度关键数据结构关系图:
3.5 组调度与cgroup的关联
组调度机制通过cgroup(Control Groups)暴露给用户空间。在cgroup v1中,cpu子系统提供了cpu.shares文件用于设置任务组的CPU权重;在cgroup v2中,cpu.weight文件承担了类似的功能。用户可以通过修改这些文件来动态调整任务组的CPU时间片分配比例。
组调度与cgroup的对应关系:
| 概念 | 内核实现 | cgroup接口 | 说明 |
|---|---|---|---|
| 任务组 | struct task_group |
cgroup目录(如 /sys/fs/cgroup/cpu/mygroup) |
每个cgroup目录对应一个内核任务组 |
| 组权重 | shares 字段 |
cpu.shares(v1) / cpu.weight(v2) |
设置任务组在CPU时间片分配中的相对权重 |
| 组内进程 | tasks 列表 |
cgroup.procs / tasks |
将进程PID写入该文件,进程即加入对应任务组 |
| CPU带宽控制 | cfs_bandwidth |
cpu.cfs_period_us / cpu.cfs_quota_us |
限制任务组在给定周期内最多使用的CPU时间(下一章详述) |
3.6 组调度的优势与适用场景
组调度相比单进程调度的核心优势:
- 多用户公平性:确保每个用户或租户获得与其权重匹配的CPU时间,不受其启动进程数量的影响。
- 容器资源隔离:每个容器可以作为一个任务组,容器内的所有进程共享该组的CPU时间片,避免一个容器内的进程风暴影响其他容器。
- 服务质量保障(QoS):可以为关键业务任务组分配较高的权重,确保其在CPU竞争中获得优先服务。
- 层级化管理:支持多级任务组嵌套,实现精细化的资源分配策略(如:部门→项目→服务→容器)。
典型适用场景:
- 云计算多租户平台:为每个租户创建一个cgroup任务组,按租户付费等级分配CPU权重。
- 容器编排(Kubernetes):每个Pod对应一个cgroup任务组,Pod内的所有容器共享CPU时间片。
- 混合负载部署:将在线服务与离线任务分别放入不同的任务组,在线服务组设置高权重,离线任务组设置低权重。
- 系统资源保护:将系统关键进程(如sshd、监控代理)放入高权重任务组,避免被用户进程完全挤占。