kernel/cgroup Cgroup 机制与原理详解
源码路径 :rk3588/kernel-6.1/kernel/cgroup/内核版本 :Linux 6.1(RK3588 平台)平台 :RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE)
该目录实现 Linux cgroup(Control Group) 子系统的核心框架 及部分内置控制器。Cgroup 将进程组织成层次化分组,对 CPU、内存、I/O、设备等资源进行限制、隔离与统计,是容器(Docker/LXC)、systemd、Android 资源管控的基础。
说明 :cgroup 采用插件化设计。本目录提供统一框架(层级管理、任务迁移、kernfs 接口、统计等),各资源控制器分散在内核其他目录实现,通过 cgroup_subsys 注册接入。
目录
一、源码目录结构 1.1 编译单元(Makefile) 1 2 3 4 5 6 7 8 obj-y := cgroup.o rstat.o namespace.o cgroup-v1.o freezer.o obj-$(CONFIG_CGROUP_FREEZER) += legacy_freezer.o obj-$(CONFIG_CGROUP_PIDS) += pids.o obj-$(CONFIG_CGROUP_RDMA) += rdma.o obj-$(CONFIG_CPUSETS) += cpuset.o obj-$(CONFIG_CGROUP_MISC) += misc.o obj-$(CONFIG_CGROUP_DEBUG) += debug.o
编译单元
源文件
规模
功能
核心框架
cgroup.c
~7,083 行
层级管理、css/css_set、迁移、kernfs、挂载
Cpuset
cpuset.c
~4,246 行
CPU/内存节点亲和性与隔离
Cgroup v1
cgroup-v1.c
~1,308 行
v1 挂载、release agent、pidlist
递归统计
rstat.c
~549 行
可扩展 per-CPU 递归资源统计
RDMA
rdma.c
~610 行
RDMA 资源限制
Misc
misc.c
~424 行
杂项资源(如 AMD SEV ASID)
Pids
pids.c
~387 行
进程数限制
Debug
debug.c
~381 行
调试控制器
Legacy Freezer
legacy_freezer.c
~487 行
v1 freezer 控制器
Freezer
freezer.c
~323 行
v2 cgroup 冻结
Namespace
namespace.c
~151 行
cgroup 命名空间
内部头文件
cgroup-internal.h
~299 行
框架内部类型与 API
1.2 子系统清单(cgroup_subsys.h) 所有控制器在 include/linux/cgroup_subsys.h 中枚举:
子系统
实现位置
功能
cpuset
kernel/cgroup/cpuset.c
CPU/内存节点绑定
cpu
kernel/sched/core.c
CFS 带宽/权重控制
cpuacct
kernel/sched/cpuacct.c
CPU 使用时间统计
io
block/blk-cgroup.c
块 I/O 带宽/权重
memory
mm/memcontrol.c
内存限制与回收
devices
security/device_cgroup.c
设备访问控制
freezer
kernel/cgroup/freezer.c
进程冻结(v2)
net_cls
net/core/
网络 classid 标记
perf_event
kernel/events/core.c
perf 事件限制
net_prio
net/core/
网络优先级
hugetlb
mm/hugetlb_cgroup.c
大页内存限制
pids
kernel/cgroup/pids.c
进程数限制
rdma
kernel/cgroup/rdma.c
RDMA 资源限制
misc
kernel/cgroup/misc.c
杂项硬件资源
debug
kernel/cgroup/debug.c
调试接口
二、整体架构 2.1 分层模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ┌─────────────────────────────────────────────────────────────┐ │ 用户空间 │ │ systemd / Docker / crictl / echo > cgroup.procs │ └──────────────────────────┬──────────────────────────────────┘ │ 读写 /sys/fs/cgroup/ (kernfs) ┌──────────────────────────▼──────────────────────────────────┐ │ cgroup.c + cgroup-v1.c (核心框架) │ │ cgroup_root │ cgroup │ css_set │ 迁移 │ 挂载 │ 文件接口 │ └──────┬──────────┬──────────┬──────────┬─────────────────────┘ │ │ │ │ ┌──────▼──┐ ┌─────▼────┐ ┌──▼──────┐ ┌─▼──────────────┐ │ cpuset │ │ cpu/mem │ │ freezer │ │ pids/rdma/ │ │ (本目录) │ │ (sched/mm)│ │ (本目录)│ │ misc (本目录)│ └──────┬──┘ └─────┬────┘ └──┬──────┘ └─┬──────────────┘ │ │ │ │ └──────────┴──────────┴──────────┘ │ ┌────────────▼────────────┐ │ task_struct │ │ task->cgroups (css_set)│ │ fork/exit 钩子 │ └─────────────────────────┘
2.2 关键锁 1 2 3 4 DEFINE_MUTEX(cgroup_mutex); DEFINE_SPINLOCK(css_set_lock); DEFINE_PERCPU_RWSEM(cgroup_threadgroup_rwsem);
锁
保护对象
cgroup_mutex
cgroup 树创建/销毁、控制器 enable/disable、迁移准备
css_set_lock
task->cgroups 指针、css_set 哈希表、任务链表
cgroup_threadgroup_rwsem
线程组级 attach 操作
2.3 默认层级 1 2 3 struct cgroup_root cgrp_dfl_root ; LIST_HEAD(cgroup_roots);
cgroup v2(default hierarchy) :单一统一层级,挂载于 /sys/fs/cgroup
cgroup v1(legacy) :可多层级,每层级绑定不同控制器组合
三、核心数据结构 3.1 struct cgroup — 控制组节点 1 2 3 4 5 6 7 8 9 10 11 12 13 struct cgroup { struct cgroup_subsys_state self ; unsigned long flags; int level; struct kernfs_node *kn ; struct cgroup_subsys_state __rcu *subsys [CGROUP_SUBSYS_COUNT ]; u16 subtree_control; u16 subtree_ss_mask; struct cgroup_root *root ; struct cgroup *dom_cgrp ; };
每个 cgroup 在文件系统中对应一个目录(如 /sys/fs/cgroup/system.slice/)。
3.2 struct cgroup_subsys_state (css) — 控制器状态 1 2 3 4 5 6 7 8 9 10 11 struct cgroup_subsys_state { struct cgroup *cgroup ; struct cgroup_subsys *ss ; struct percpu_ref refcnt ; struct list_head sibling ; struct list_head children ; struct cgroup_subsys_state *parent ; int id; u64 serial_nr; };
每个 (cgroup, subsystem) 对一个 css 实例。例如启用 memory 控制器的 cgroup 有 memory css。
3.3 struct css_set — 任务 cgroup 成员关系 1 2 3 4 5 6 7 8 9 struct css_set { struct cgroup_subsys_state *subsys [CGROUP_SUBSYS_COUNT ]; refcount_t refcount; struct css_set *dom_cset ; struct cgroup *dfl_cgrp ; struct list_head tasks ; struct list_head cgrp_links ; };
设计要点 :一个任务的 task->cgroups 指向一个 css_set。多个任务可共享同一 css_set(当它们在所有层级上的 cgroup 成员关系完全相同时),fork 时只需增减 css_set 引用计数,避免逐控制器更新。
3.4 struct cgroup_root — 层级根 1 2 3 4 5 6 7 8 9 struct cgroup_root { struct kernfs_root *kf_root ; struct cgroup cgrp ; int hierarchy_id; u16 subsys_mask; unsigned int flags; char release_agent_path[PATH_MAX]; };
3.5 struct cgroup_subsys — 控制器插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct cgroup_subsys { struct cgroup_subsys_state *(*css_alloc )(struct cgroup_subsys_state *parent_css ); int (*css_online)(struct cgroup_subsys_state *css); void (*css_offline)(struct cgroup_subsys_state *css); void (*css_free)(struct cgroup_subsys_state *css); int (*can_attach)(struct cgroup_taskset *tset); void (*attach)(struct cgroup_taskset *tset); void (*fork)(struct task_struct *task); void (*exit )(struct task_struct *task); struct cftype *dfl_cftypes ; struct cftype *legacy_cftypes ; const char *name; bool implicit_on_dfl; bool threaded; };
3.6 M:N 关系 — cgrp_cset_link 1 2 3 cgroup A ←── cgrp_cset_link ──→ css_set X ←── task 1, task 2 cgroup B ←── cgrp_cset_link ──→ css_set X cgroup C ←── cgrp_cset_link ──→ css_set Y ←── task 3
一个 css_set 可关联多个 cgroup(v1 多层级),一个 cgroup 也可关联多个 css_set(不同任务组合)。
四、Cgroup v1 与 v2 4.1 对比
特性
cgroup v1
cgroup v2(Unified)
层级数量
多个独立层级
单一统一层级
控制器绑定
挂载时绑定到层级
通过 cgroup.subtree_control 按需启用
进程规则
父子可同时有进程
默认 domain 模式:非 root 内部不能有进程
接口路径
每控制器独立文件
统一 cgroup.* 接口
挂载点
/sys/fs/cgroup/<controller>
/sys/fs/cgroup
文件系统
cgroup
cgroup2
Freezer
legacy_freezer(独立控制器)
内置 cgroup.freeze
4.2 v2 Domain 与 Threaded 模式
Domain cgroup :资源隔离边界,可启用所有控制器,内部不应直接持有进程(leaf 除外)
Threaded cgroup :仅支持 cpu、memory、pids、perf_event 等 threaded 控制器,用于线程级分组
通过 cgroup.type 文件设置:domain、domain threaded、threaded。
4.3 v2 控制器启用流程 1 2 3 4 5 6 echo "+memory +cpu" > /sys/fs/cgroup/cgroup.subtree_controlecho "max 200000" > /sys/fs/cgroup/mygroup/cpu.maxecho "256M" > /sys/fs/cgroup/mygroup/memory.max
五、子系统注册与生命周期 5.1 注册机制 1 2 3 4 struct cgroup_subsys *cgroup_subsys [] = {#include <linux/cgroup_subsys.h> };
每个控制器在各自源文件中定义:
1 2 3 4 5 6 7 8 9 10 11 12 struct cgroup_subsys cpu_cgrp_subsys = { .css_alloc = cpu_css_alloc, .css_online = cpu_css_online, .css_offline = cpu_css_offline, .css_free = cpu_css_free, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, .fork = cpu_cgroup_fork, .dfl_cftypes = cpu_files, .name = "cpu" , };
5.2 css 生命周期 1 2 3 4 5 6 7 8 9 cgroup 创建子目录 → css_alloc(parent_css) // 分配子 css → css_online(css) // 激活,CSS_ONLINE 置位 → 用户写入接口文件配置限制 cgroup 删除(rmdir) → css_offline(css) // 停用,拒绝新任务 → css_released(css) // 引用归零 → css_free(css) // 释放(可能异步 via workqueue)
5.3 任务生命周期钩子 1 2 3 4 5 6 7 void cgroup_fork (struct task_struct *p) ; int cgroup_can_fork (...) ; void cgroup_post_fork (...) ; void cgroup_exit (struct task_struct *p) ; void cgroup_release (struct task_struct *p) ;void cgroup_free (struct task_struct *p) ;
六、任务迁移机制 6.1 迁移 API 1 2 3 int cgroup_attach_task (struct cgroup *dst_cgrp, struct task_struct *leader, bool threadgroup) ;
6.2 迁移流程 1 2 3 4 5 6 7 8 9 10 cgroup_attach_task() ├── cgroup_migrate_add_src() // 收集源 css_set ├── cgroup_migrate_prepare_dst() // 预创建/查找目标 css_set └── cgroup_migrate() ├── cgroup_migrate_add_task() // 任务加入 mg_tasks 链表 └── cgroup_migrate_execute() ├── can_attach() // 各控制器预检查 ├── css_set_move_task() // 切换 task->cgroups(commit) ├── attach() // 通知控制器迁移完成 └── post_attach()
核心 commit 点(cgroup_migrate_execute):
1 2 3 4 5 6 7 8 spin_lock_irq(&css_set_lock); list_for_each_entry(cset, &tset->src_csets, mg_node) { list_for_each_entry_safe(task, tmp_task, &cset->mg_tasks, cg_list) { css_set_move_task(task, from_cset, to_cset, true ); cgroup_freezer_migrate_task(task, from_cset->dfl_cgrp, to_cset->dfl_cgrp); } } spin_unlock_irq(&css_set_lock);
原子性保证 :所有 can_attach() 通过后才会 commit;任一失败则全部回滚。
6.3 常用迁移方式
方式
接口
说明
写入 procs
echo PID > cgroup.procs
迁移进程(thread group leader)
写入 threads
echo PID > cgroup.threads
迁移单个线程
clone 直接加入
clone3(CLONE_INTO_CGROUP)
创建时直接进入目标 cgroup
批量迁移
cgroup.transfer_tasks
v1 控制器间迁移
七、递归统计 rstat 7.1 设计目标 rstat.c 实现 可扩展递归统计(scalable recursive statistics) :
每个 cgroup 维护 per-CPU 统计(cgroup_rstat_cpu)
写入时仅标记本 cgroup 为 “updated”
读取时惰性向上传播 到祖先,复杂度 O(活跃子树大小) 而非 O(总子树)
7.2 数据结构 1 2 3 4 5 6 7 struct cgroup_rstat_cpu { struct u64_stats_sync bsync ; struct cgroup_base_stat bstat ; struct cgroup_base_stat last_bstat ; struct cgroup *updated_children ; struct cgroup *updated_next ; };
7.3 使用场景
cgroup.stat — 显示 CPU 使用统计
memory.current、memory.peak 等(memory 控制器扩展 rstat)
PSI(Pressure Stall Information)压力指标
八、本目录内置控制器 8.1 Cpuset(cpuset.c) 最复杂的内置控制器(~4,246 行),管理 CPU 和内存节点亲和性:
接口文件
功能
cpuset.cpus
允许使用的 CPU 集合
cpuset.mems
允许使用的内存节点
cpuset.cpu_exclusive
CPU 独占
cpuset.mem_exclusive
内存节点独占
cpuset.cpus.effective
有效 CPU(考虑父级约束)
RK3588 big.LITTLE 场景下,cpuset 可将任务绑定到 A76 或 A55 核心簇。
8.2 Freezer(freezer.c + legacy_freezer.c) v2 freezer (freezer.c):
接口
功能
cgroup.freeze
写 1 冻结 cgroup 内所有任务
cgroup.events
读取 frozen 状态
冻结机制:设置 CGRP_FREEZE 标志,对任务发送 SIGSTOP,CGRP_FROZEN 表示实际已冻结。支持递归:所有子 cgroup 都 frozen 时父 cgroup 也标记 frozen。
v1 legacy_freezer (legacy_freezer.c):独立的 freezer 控制器,freezer.state 文件。
8.3 Pids(pids.c) 限制 cgroup 内进程数量:
接口
功能
pids.max
最大进程数(”max” = 无限制)
pids.current
当前进程数
pids.events
超限事件计数
fork() 时通过 can_fork() 检查,超限返回 -EAGAIN。
8.4 RDMA(rdma.c) 限制 RDMA 资源使用:
资源
说明
hca_handle
HCA 句柄数
hca_object
HCA 对象数
按 cgroup × RDMA 设备维护 resource pool。
8.5 Misc(misc.c) 杂项硬件资源限制(如 AMD SEV/SEV-ES ASID 数量),采用 Limits 分配模型。
8.6 Debug(debug.c) 调试控制器,仅在 cgroup_debug=1 时启用,提供额外诊断接口。
九、分散实现的控制器 9.1 CPU 控制器(kernel/sched/core.c)
接口
功能
cpu.max
CFS 带宽上限(v2)
cpu.weight
相对权重(默认 100)
cpu.stat
usage、user、system 等
cpu.pressure
CPU PSI 压力
与 CFS 调度器深度集成,通过 task_group 实现带宽控制。
9.2 Memory 控制器(mm/memcontrol.c)
接口
功能
memory.max
内存硬上限
memory.high
内存软上限(触发 reclaim)
memory.current
当前使用量
memory.swap.max
swap 上限
memory.events
oom、max 等事件
memory.stat
详细内存统计
通过 mem_cgroup 结构跟踪 page cache、anon、swap 等。
9.3 IO 控制器(block/blk-cgroup.c)
接口
功能
io.max
设备级 I/O 带宽/IOPS 上限
io.weight
相对 I/O 权重
io.stat
读写统计
io.pressure
I/O PSI 压力
9.4 其他
控制器
位置
功能
cpuacct
kernel/sched/cpuacct.c
CPU 时间记账(v1 为主)
devices
security/device_cgroup.c
字符/块设备访问 whitelist
net_cls
net/core/
设置 sk->classid
net_prio
net/core/
设置 socket 优先级
hugetlb
mm/hugetlb_cgroup.c
大页内存限制
perf_event
kernel/events/core.c
perf 事件作用域
十、Cgroup Namespace namespace.c 实现 cgroup namespace(CLONE_NEWCGROUP):
1 2 3 4 5 struct cgroup_namespace { struct ns_common ns ; struct user_namespace *user_ns ; struct css_set *root_cset ; };
容器内 /proc/self/cgroup 显示相对于 namespace root 的路径
创建需要 CAP_SYS_ADMIN
与 mount namespace 配合实现容器 cgroup 视图隔离
十一、用户空间接口 11.1 v2 统一接口(每个 cgroup 目录)
文件
功能
cgroup.procs
读写:迁移进程
cgroup.threads
读写:迁移线程
cgroup.controllers
只读:可用控制器
cgroup.subtree_control
读写:子 cgroup 启用的控制器
cgroup.type
domain / threaded 类型
cgroup.max.depth
最大子 cgroup 深度
cgroup.max.descendants
最大子 cgroup 数量
cgroup.stat
CPU 使用统计
cgroup.freeze
冻结/解冻
cgroup.events
frozen、populated 等事件
cpu.max / memory.max / …
各控制器限制
11.2 常用操作示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 mkdir /sys/fs/cgroup/myappecho "+memory +cpu" > /sys/fs/cgroup/cgroup.subtree_controlecho "256M" > /sys/fs/cgroup/myapp/memory.maxecho "50000 100000" > /sys/fs/cgroup/myapp/cpu.max echo $$ > /sys/fs/cgroup/myapp/cgroup.procscat /proc/self/cgroupecho 1 > /sys/fs/cgroup/myapp/cgroup.freezeecho "4-7" > /sys/fs/cgroup/myapp/cpuset.cpusecho 0 > /sys/fs/cgroup/myapp/cpuset.memsecho $$ > /sys/fs/cgroup/myapp/cgroup.procs
11.3 bpffs 与 cgroup BPF cgroup BPF 程序(kernel/bpf/cgroup.c)可附加到 cgroup 的多种 hook 点(ingress/egress/bind/connect 等),实现网络策略。挂载通过 BPF_LINK_CREATE 或 cgroup 目录下的 BPF 接口。
十二、完整任务加入 Cgroup 时序 以 v2 写入 cgroup.procs 为例:
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 [用户空间] echo 1234 > /sys/fs/cgroup/mygroup/cgroup.procs [内核 - VFS/kernfs] cgroup_procs_write() → cgroup_attach_task(mygroup, task, true) [内核 - 迁移准备] cgroup_migrate_add_src(task_css_set(task), mygroup, &mgctx) cgroup_migrate_prepare_dst(&mgctx) → 查找或创建目标 css_set [内核 - 预检查] cgroup_migrate_execute(&mgctx) → memory_can_attach() // 检查 memory 限制 → cpu_can_attach() // 检查 cpu 约束 → pids_can_attach() // 检查 pids 限制 → cpuset_can_attach() // 检查 CPU 亲和性 [内核 - Commit] → css_set_move_task(task, from, to, true) → task->cgroups = to_cset → cgroup_freezer_migrate_task() // 处理 freeze 状态 [内核 - 通知控制器] → memory_attach() // 更新 memcg 计数 → cpu_attach() // 更新 task_group → cpuset_attach() // 更新 CPU 亲和性 mask [内核 - 调度影响] 下次 schedule() 时 task 在新 cgroup 的 CPU 权重/带宽下运行 内存分配计入新 memcg
十三、RK3588 / Android 平台应用 13.1 典型 cgroup 层级(Android/systemd) 1 2 3 4 5 6 7 /sys/fs/cgroup/ ├── init.scope ├── system.slice/ │ ├── docker.service/ │ └── ... ├── user.slice/ └── ...
Android 使用 cgroup v2 管理应用进程资源(通过 libprocessgroup):
场景
使用的控制器
应用内存限制
memory.max
CPU 带宽控制
cpu.max
后台冻结
cgroup.freeze
核心绑定(游戏/性能模式)
cpuset.cpus
进程数限制
pids.max
13.2 RK3588 big.LITTLE cpuset 配置 1 2 3 4 5 6 7 8 echo "4-7" > /sys/fs/cgroup/performance/cpuset.cpusecho "0-3" > /sys/fs/cgroup/background/cpuset.cpuscat /sys/fs/cgroup/mygroup/cpuset.cpus.effective
13.3 调试命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 cat /proc/<PID>/cgroupcat /sys/fs/cgroup/<path>/cgroup.procscat /sys/fs/cgroup/<path>/memory.currentcat /sys/fs/cgroup/<path>/memory.statcat /sys/fs/cgroup/<path>/cpu.statcat /sys/fs/cgroup/<path>/memory.pressurecat /sys/fs/cgroup/<path>/cpu.pressurecat /sys/fs/cgroup/<path>/io.pressure
13.4 确认配置 1 2 zcat /proc/config.gz | grep -E 'CGROUP|CPUSET|MEMCG|BLK_CGROUP' mount | grep cgroup
RK3588 典型配置:
CONFIG_CGROUPS=y
CONFIG_CGROUP_V2=y(默认 unified hierarchy)
CONFIG_MEMCG=y
CONFIG_CGROUP_SCHED=y(cpu 控制器)
CONFIG_BLK_CGROUP=y(io 控制器)
CONFIG_CPUSETS=y
十四、总结 kernel/cgroup 是 Linux 资源管控的核心基础设施:
统一框架 (cgroup.c)— 层级管理、css_set 优化、任务迁移、kernfs 接口
插件化控制器 — 通过 cgroup_subsys 注册,分散在各子系统实现
v2 Unified Hierarchy — 单一层级、按需启用控制器、domain/threaded 模式
css_set 共享 — 相同 cgroup 组合的任务共享 css_set,优化 fork 性能
原子迁移 — can_attach → commit → attach 三阶段保证一致性
rstat — 高效递归统计,支撑 cgroup.stat 和 PSI
在 RK3588 / Android 平台上,cgroup 是进程资源隔离与限制的基础,与调度器(cpu)、内存管理(memory)、块 I/O(io)深度协同,是容器化和系统资源管控的基石。
附录:源文件完整清单
文件
行数
分类
cgroup.c
7,083
核心框架
cpuset.c
4,246
Cpuset 控制器
cgroup-v1.c
1,308
v1 兼容
rdma.c
610
RDMA 控制器
rstat.c
549
递归统计
legacy_freezer.c
487
v1 Freezer
misc.c
424
Misc 控制器
pids.c
387
Pids 控制器
debug.c
381
Debug 控制器
freezer.c
323
v2 Freezer
cgroup-internal.h
299
内部头文件
namespace.c
151
Cgroup namespace
正在加载留言…