4. cgroup v1与v2概述
cgroup(Control Group)是 Linux 内核提供的一种机制,用于对进程组进行资源限制、优先级控制、审计和隔离。自 2008 年合并入内核主线以来,cgroup 经历了从 v1 到 v2 的重大演进。理解这两个版本的层次结构、子系统划分以及核心差异,是掌握组调度与 CPU 带宽控制的基础。
4.1 cgroup 的层次结构
cgroup 的核心设计围绕“树形层次结构”展开。每个 cgroup 代表一个任务组,可以包含零个或多个子 cgroup,形成一棵从根节点向下延伸的树。进程可以隶属于某个 cgroup 节点,并继承该节点的资源约束。
关键概念: 在 cgroup v1 中,每个子系统可以挂载到独立的树,形成多树并行的结构;而在 cgroup v2 中,所有子系统统一挂载到一棵统一的树,消除了多树带来的混乱。
4.2 子系统(Subsystem)详解
子系统是 cgroup 中实际执行资源控制的模块。每个子系统负责一种特定的资源类型。下表列出了 v1 和 v2 中主要的子系统及其功能对比。
| 子系统名称 | 控制资源 | v1 支持 | v2 支持 | 关键接口文件(v2) |
|---|---|---|---|---|
| cpu | CPU 时间分配与带宽限制 | ✓ | ✓ | cpu.weight, cpu.max |
| memory | 内存使用上限与回收策略 | ✓ | ✓ | memory.max, memory.high |
| io | 块设备 I/O 带宽与 IOPS | ✓(blkio) | ✓ | io.max, io.weight |
| cpuset | CPU 核与内存节点绑定 | ✓ | ✓ | cpuset.cpus, cpuset.mems |
| pids | 进程/线程数量上限 | ✓ | ✓ | pids.max |
| net_cls | 网络包分类标记 | ✓ | ✗ | — |
| net_prio | 网络优先级 | ✓ | ✗ | — |
| hugetlb | 大页内存使用限制 | ✓ | ✓ | hugetlb.1GB.max |
| rdma | RDMA 资源限制 | ✓ | ✓ | rdma.max |
| misc | 其他杂项资源(如 perf_event) | 部分 | ✓ | misc.max |
注意: cgroup v2 移除了
net_cls 和 net_prio 子系统,网络 QoS 控制改由 eBPF 和 TC 等更灵活的机制实现。同时,v2 将 blkio 重命名为 io,并统一了接口语义。
4.3 cgroup v1 与 v2 的核心差异
从 v1 到 v2 的演进不仅仅是接口的简化,更是设计哲学的根本转变。以下是两者在架构、接口、安全性和可维护性方面的关键差异。
- 树结构: v1 允许多树并行,每个子系统独立挂载;v2 强制统一树,所有子系统共享一个层次结构。
- 进程归属: v1 中一个进程可以属于不同树的不同 cgroup;v2 中一个进程只能属于一个 cgroup,所有资源控制在该节点内统一配置。
- 线程粒度: v1 支持线程级迁移(threaded mode),但实现复杂;v2 默认以进程为单位,但通过
cgroup.threads文件支持线程模式。 - 接口一致性: v2 统一了资源限制文件的命名规范(如
.max、.weight、.high),降低了学习成本。 - 内部竞争消除: v2 禁止在同一个 cgroup 内同时存在子 cgroup 和进程(即“内部进程竞争”),确保资源控制的可预测性。
- 安全性: v2 强化了 delegation(委派)模型,非特权用户可以在自己的 cgroup 子树内进行资源管理,无需全局 root 权限。
4.4 从 v1 迁移到 v2 的注意事项
当前主流发行版(如 RHEL 9、Ubuntu 22.04+)已默认使用 cgroup v2。如果系统仍运行 v1,迁移时需关注以下要点:
- 内核参数: 通过
systemd.unified_cgroup_hierarchy=1启用 v2,或通过cgroup_no_v1=all禁用 v1 子系统。 - 兼容性: 部分旧版容器运行时(如 Docker < 20.10)可能不完全支持 v2,需升级。
- 监控工具: 基于 v1 接口的监控脚本(如读取
memory.usage_in_bytes)需要调整为 v2 接口(如memory.current)。 - 混合模式: 内核支持 v1 和 v2 共存(hybrid mode),但建议生产环境统一使用 v2 以避免混淆。
警告: 在混合模式下,某些子系统可能同时出现在 v1 和 v2 树中,导致资源控制冲突。务必通过
mount | grep cgroup 确认当前挂载状态,避免配置覆盖。
4.5 实战:检查当前系统的 cgroup 版本
在开始配置之前,首先确认你的系统运行的是 cgroup v1 还是 v2。以下命令可以快速判断:
# 方法1:检查 /sys/fs/cgroup 的挂载类型
stat -fc %T /sys/fs/cgroup/
# 输出 "cgroup2fs" 表示 v2,输出 "tmpfs" 表示 v1
# 方法2:查看内核参数
cat /proc/cmdline | grep -o 'systemd.unified_cgroup_hierarchy=[01]'
# 1 表示 v2,0 表示 v1
# 方法3:检查 cgroup 版本文件
cat /sys/fs/cgroup/cgroup.controllers 2>/dev/null && echo "v2" || echo "v1"
# 如果文件存在且可读,则为 v2