博文内容为:cgroup介绍,cgroup V1和V2的区别,cgroup三组件以及如何使用cgroup V2。
1.什么是cgroup?
Linux内核负责系统中所有硬件的可靠交互。这不仅包括使操作系统理解硬件的必要代码,还涉及限制特定程序对系统资源的占用。内核必须将系统内存合理分配给各个应用程序,以确保计算机的正常运行。通常情况下,Linux系统可以让大多数应用程序无限制地运行,而不会出现问题,因为应用程序间能够良好地协同工作。但是,如果某个程序出现故障并开始占用所有可用内存,会发生什么呢?
为应对此类情况,内核引入了OOM(Out Of Memory) Killer机制。在系统内存耗尽前,OOM Killer会主动终止某些应用程序,以释放足够的内存,防止系统崩溃。因此,OOM Killer是系统在崩溃前的最后一道防线,能够在关键时刻帮助系统恢复正常。不过,由于内核可以决定哪些进程会被OOM保护,它也能够预先限制某些应用程序的内存使用,避免它们占用过多资源。
所以如何去有效分配和控制资源就变得尤为重要。cgroup(Control Group)便是为了解决这一问题而诞生的。最早由Google工程师开发并在Linux内核中引入的cgroup,为系统管理员和开发人员提供了强大的资源控制工具,可以精细化管理资源的分配和使用。通过cgroup,我们可以限制进程的资源使用,避免某些进程消耗过多资源,保障系统的稳定运行。
这些资源包括 CPU、内存、存储、网络等。通过 cgroups 可以方便地限制某个进程的资源占用,并且可以实时地监控进程的监控与统计信息。
2.cgroup v1和v2
cgroup在实现上是一种分层组织进程的进制,沿层次以受控的方式分配CPU、内存、网络、存储等系统资源。在本机下执行以下命令可以查看当前机器的cgroup都支持对哪些资源进行控制。
zzxy@zzxy-virtual-machine:~$ lssubsys -a
cpuset,cpu,cpuacct,blkio,memory,devices,freezer,net_cls,perf_event,net_prio,hugetlb,pids,rdma,misc
其中cpu和cpuset都是对cpu资源进行控制的子系统。cpu是通过执行时间来控制进程对cpu的使用,cpuset是通过分配逻辑核的方式来分配,其他可控制的资源还包括内存,设备,网络数据流,网络优先级,限制大页面等等。
cgroup控制器的发展经历了cgroup v1和cgroup v2两个阶段。
我们看一下cgroup v1和cgroup v2是如何沿着层次结构分配系统资源的。在cgroup v1的接口中,对每一种资源都存在一个层级,如图所示:

cgroup v1在每种资源对应的层级上都有一个根节点,代表的是该种资源的全部分配。在其下面的子节点是对这种资源进行的限制和划分。子节点下还可以再继续派生孙节点。孙节点又是子节点所拥有的资源限制的进一步划分,依此类推。对于某一个特定的用户进程,如果想对其所使用的CPU、内存、网络进行限制,那就把进程和指定资源下的某个cgroup建立起关联关系。每一种资源都如此操作一遍,就建立起了对所有类型资源的限制,如图所示:

但是在cgroup v2中,将这个层级结构进行了简化。整个cgroup系统只有一个层级。每个节点上都可以拥有对cpu、内存、网络等多种资源的限制。父节点开启的子系统控制器可以控制到儿子节点。如果父节点开启了cpu、内存控制,那么其节点的cgroup就可以限制其关联进程的cpu/内存的使用量,如下图所示:

想观察这些层级结构也非常容易,cgroup一般是挂载到/sys/fs/cgroup目录下的。进入该目录便可以看到每一种资源的根cgroup:
cd /sys/fs/cgroup/
zzxy@zzxy-virtual-machine:/sys/fs/cgroup$ ls -al
总计 0
dr-xr-xr-x 12 root root 0 10月 28 10:52 .
drwxr-xr-x 8 root root 0 10月 28 10:52 ..
-r--r--r-- 1 root root 0 10月 29 22:50 cgroup.controllers
-rw-r--r-- 1 root root 0 10月 29 22:50 cgroup.max.depth
-rw-r--r-- 1 root root 0 10月 29 22:50 cgroup.max.descendants
-rw-r--r-- 1 root root 0 10月 29 22:50 cgroup.pressure
-rw-r--r-- 1 root root 0 10月 29 22:50 cgroup.procs
-r--r--r-- 1 root root 0 10月 29 22:50 cgroup.stat
-rw-r--r-- 1 root root 0 10月 28 12:43 cgroup.subtree_control
-rw-r--r-- 1 root root 0 10月 29 22:50 cgroup.threads
-rw-r--r-- 1 root root 0 10月 29 22:50 cpu.pressure
-r--r--r-- 1 root root 0 10月 29 22:50 cpuset.cpus.effective
-r--r--r-- 1 root root 0 10月 29 22:50 cpuset.cpus.isolated
-r--r--r-- 1 root root 0 10月 29 22:50 cpuset.mems.effective
-r--r--r-- 1 root root 0 10月 29 22:50 cpu.stat
-r--r--r-- 1 root root 0 10月 29 22:50 cpu.stat.local
.....
-rw-r--r-- 1 root root 0 10月 29 22:50 io.prio.class
-r--r--r-- 1 root root 0 10月 29 22:50 io.stat
-r--r--r-- 1 root root 0 10月 29 22:50 memory.numa_stat
-rw-r--r-- 1 root root 0 10月 29 22:50 memory.pressure
--w------- 1 root root 0 10月 29 22:50 memory.reclaim
-r--r--r-- 1 root root 0 10月 29 22:50 memory.stat
-rw-r--r-- 1 root root 0 10月 29 22:50 memory.zswap.writeback
-r--r--r-- 1 root root 0 10月 29 22:50 misc.capacity
-r--r--r-- 1 root root 0 10月 29 22:50 misc.current
drwxr-xr-x 2 root root 0 10月 28 10:52 proc-sys-fs-binfmt_misc.mount
drwxr-xr-x 2 root root 0 10月 28 10:52 sys-fs-fuse-connections.mount
.......
- 如上,在cgroup v2中,进程只需要和一个cgroup建立关联关系。在cgroup v2的sys/fs/cgroup目录下,不再区分CPU、memory等多个层级(多个目录),而是只需要一个层级,比如:--cgroup.controllers:列出了当前支持的控制器(如
cpu、memory、io等),这些控制器用于管理进程的资源使用。 - cgroup.max.depth:配置或显示此控制组层级的最大深度,用于限制子组层级的数量。
- cgroup.pressure、cpu.pressure、memory.pressure:这些文件用于跟踪资源压力信息,通常反映 CPU、内存等资源的压力情况(如任务阻塞、资源竞争等)。
但是无论是cgroup v1还是cgroup v2,都可以实现对进程要用的cpu、内存等系统资源合理设置使用限制。
3.cgroup三组件
Cgroups 主要包括下面几部分:
-
cgroups 本身:cgroup 是对进程分组管理的一种机制,一个 cgroup 包含一组进程,并可以在这个 cgroup 上增加 Linux subsystem 的各种参数配置,将一组进程和一组 subsystem 的系统参数关联起来。
-
subsystem: 一个子系统其实就是一种资源的控制器,比如memory子系统可以控制进程内存的使用。子系统需要加入到某个层级,然后该层级的所有控制组,均受到这个子系统的控制。
-
hierarchy:一个 hierarchy 可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与零到多个 subsystem 关联。在一颗树里面,会包含 Linux 系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗 cgroup 树,每棵树都和不同的 subsystem 关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的 subsystem 关联。目前 Linux 支持 12 种 subsystem,如果不考虑不与任何 subsystem 关联的情况(systemd 就属于这种情况),Linux 里面最多可以建 12 颗 cgroup 树,每棵树关联一个 subsystem,当然也可以只建一棵树,然后让这棵树关联所有的 subsystem。当一颗 cgroup 树不和任何 subsystem 关联的时候,意味着这棵树只是将进程进行分组,至于要在分组的基础上做些什么,将由应用程序自己决定,systemd 就是一个这样的例子。
3 个部分间的关系
-
系统在创建了新的 hierarchy 之后,系统中所有的进程都会加入这个 hierarchy 的 cgroup 根节点,这个 cgroup 根节点是 hierarchy 默认创建的。
-
一个 subsystem 只能附加到 一 个 hierarchy 上面。
-
一个 hierarchy 可以附加多个 subsystem 。
-
一个进程可以作为多个 cgroup 的成员,但是这些 cgroup 必须在不同的 hierarchy 中。
-
一个进程 fork 出子进程时,子进程是和父进程在同一个 cgroup 中的,也可以根据需要将其移动到其他 cgroup 中。

两个任务组成了一个 Task Group,并使用了 CPU 和 Memory 两个子系统的 cgroup,用于控制 CPU 和 MEM 的资源隔离。
4.如何使用cgroup?
现在通过一个实验去限制某个进程的CPU和内存资源,然后去观察资源控制效果。
cgroups 以文件的方式提供应用接口,我们可以通过 mount 命令来查看 cgroups 默认的挂载点:
zzxy@zzxy-virtual-machine:~$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
这个表示当前系统使用了 cgroup2 文件系统,并将其挂载在 /sys/fs/cgroup 路径上,以下是对各个选项的解释:
cgroup2:表示使用的是cgroup v2版本。/sys/fs/cgroup:这是cgroup2的挂载点,所有的控制组操作都在这个目录下进行。type cgroup2:表示文件系统类型为cgroup2。(rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot):这是挂载选项,具体解释如下:rw:允许读写。nosuid:禁止在该文件系统上使用set-user-ID和set-group-ID位。nodev:在此文件系统上禁用字符设备和块设备的访问。noexec:禁止在该文件系统上执行任何文件。relatime:仅在文件上次访问时间比上次修改或状态改变时间更新的情况下更新文件的访问时间戳,减少写操作以提高性能。nsdelegate:允许非根用户命名空间的任务委托对 cgroup 资源的管理权。memory_recursiveprot:这是cgroup2特有的选项,用于内存控制。如果启用此选项,子控制组将继承父控制组的内存保护,形成递归的保护机制。
1.查看subsystem 列表
可以通过查看/proc/cgroups知道当前系统支持哪些 subsystem:
zzxy@zzxy-virtual-machine:~$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 0 119 1
cpu 0 119 1
cpuacct 0 119 1
blkio 0 119 1
memory 0 119 1
devices 0 119 1
freezer 0 119 1
net_cls 0 119 1
perf_event 0 119 1
net_prio 0 119 1
hugetlb 0 119 1
pids 0 119 1
rdma 0 119 1
misc 0 119 1
各字段的含义如下:
- subsys_name:子系统名称,表示控制组可以管理的资源类型(例如
cpuset、cpu、memory等)。 - hierarchy:层级 ID,表示控制组层次结构。0 表示使用的是统一的层级结构,即该机器使用的cgroup V2。
- num_cgroups:当前系统中创建的控制组的数量。
- enabled:表示该子系统是否已启用。
2.使用cgroup2
进入/sys/fs/cgroup目录下,使用root权限创建自己的mycgroup:
mkdir mycgroup
可以查看mycgroup可用的控制器:
root@zzxy-virtual-machine:/sys/fs/cgroup# cd mycgroup/
root@zzxy-virtual-machine:/sys/fs/cgroup/mycgroup# ls
cgroup.controllers cgroup.pressure cpu.idle cpuset.cpus.exclusive cpu.stat.local io.max memory.current memory.min memory.stat memory.zswap.current pids.max
cgroup.events cgroup.procs cpu.max cpuset.cpus.exclusive.effective cpu.uclamp.max io.pressure memory.events memory.numa_stat memory.swap.current memory.zswap.max pids.peak
cgroup.freeze cgroup.stat cpu.max.burst cpuset.cpus.partition cpu.uclamp.min io.prio.class memory.events.local memory.oom.group memory.swap.events memory.zswap.writeback
cgroup.kill cgroup.subtree_control cpu.pressure cpuset.mems cpu.weight io.stat memory.high memory.peak memory.swap.high pids.current
cgroup.max.depth cgroup.threads cpuset.cpus cpuset.mems.effective cpu.weight.nice io.weight memory.low memory.pressure memory.swap.max pids.events
cgroup.max.descendants cgroup.type cpuset.cpus.effective cpu.stat io.latency irq.pressure memory.max memory.reclaim memory.swap.peak pids.events.local
root@zzxy-virtual-machine:/sys/fs/cgroup/mycgroup# cat cgroup.controllers
cpuset cpu io memory pids
每个child cgroup不会继承其父母的控制器,只有在父节点cgroup.subtree_control显式配置开启的控制器才能在child cgroup中使用。目前mycgroup可以对cpuset, cpu, io, memroy, pids这些资源进行限制。
对cpu进行资源限制
通过如下命令对自己的cgroup添加cpu资源限制,这表示在每 100ms 的周期内,mycgroup 中的任务最多可以使用 50ms 的 CPU 时间。
echo 5000 10000 > cpu.max
创建一个无限循环的脚本占用CPU:
vim test.sh
while :
do
:
done
使用./test.sh &运行并获取该进程的pid,使用top -p <pid>观察其cpu使用率:

我们使用如下命令将该进程加入我们创建的cgroup里:
echo pid | sudo tee /sys/fs/cgroup/mycgroup/cgroup.procs
此时再观察其使用率:

发现在我们设定的50%以下。
4320

被折叠的 条评论
为什么被折叠?



