文章目录
Cgroup简介
- Cgroup 是 Linux 内核的一个特性,用于对进程组的资源(如 CPU、内存、磁盘 I/O 等)进行限制、监控和管理。
Cgroup核心概念
Cgroup(Control Group)定义
-
每个 cgroup 是一个进程组,可以包含多个进程,该进程组有很多属性(这些属性需要通过关联子系统的方式附加上去(挂载),挂载完成后就得到了很多cgroup文件),用于限制、监控和管理该进程组的资源。
-
cgroup 是对进程分组管理的一种机制,一个 cgroup 包含一组进程,并可以在这个 cgroup 上增加 Linux subsystem 的各种参数配置,将一组进程和一组 subsystem 的系统参数关联起来。
核心功能
将进程分组,并对这些组的资源使用进行控制。
特点
- 每个 cgroup 是一个进程组,可以包含多个进程。
- cgroup 可以嵌套,形成层次结构(树状结构)。
- 每个 cgroup 可以关联一个或多个子系统(Subsystem),用于管理特定类型的资源。
Cgroup 树(Hierarchy)定义
- Cgroup 树是 cgroup 的层次结构,由多个 cgroup 节点组成,形成一个树状结构。
特点
- 每个 cgroup 树是一个独立的层次结构,可以关联一个或多个子系统。
- 一个系统中可以存在多个 cgroup 树,每个树可以管理不同的资源。
- 每个进程只能属于一个 cgroup 树中的一个 cgroup 节点,但可以同时属于多个 cgroup 树(每个树关联不同的子系统)。
- 可以在根 cgroup 下可以设置一些全局的资源限制,而在子 cgroup 中又可以针对特定的进程组设置更具体、更细致的限制。这种层次结构使得系统能够根据不同的需求对进程进行分层管理,实现资源的精细分配。
作用
- 通过树状结构,实现资源的层次化分配和管理。
- 父 cgroup 的资源限制会传递给子 cgroup。
Subsystem(子系统)定义
- 子系统是 cgroup 中负责管理特定类型资源的模块,也称为控制器(Controller)。目前大约有12 - 14个常见的子系统。
常见子系统
- cpu:控制 CPU 时间分配。
- memory:控制内存使用。
- blkio:控制块设备 I/O(如磁盘带宽)。
- net_cls:控制网络流量分类。
- freezer:暂停和恢复 cgroup 中的进程。
- pids:限制 cgroup 中的进程数量。
特点
- 每个子系统可以绑定到一个 cgroup 树。
- 一个 cgroup 树可以绑定多个子系统。
- 子系统的资源控制是独立的,不同子系统可以管理不同的资源。
查看子系统
$ cat /proc/cgroups
#subsys_name hierarchy num_cgroups enabled
cpuset 1 1 1
cpu 2 57 1
cpuacct 3 1 1
blkio 4 36 1
memory 5 98 1
devices 6 50 1
freezer 7 2 1
net_cls 8 1 1
perf_event 9 1 1
net_prio 10 1 1
hugetlb 11 1 1
pids 12 63 1
rdma 13 1 1
misc 14 1 1
- subsys_name:子系统的名字,比如 cpu 是管理 CPU 资源的,memory 是管理内存资源的。
- hierarchy:子系统关联的 cgroup 树的 ID。如果为 0,表示没有关联任何树;如果 cpu 和 cpuacct 的 hierarchy 值相同,说明它们绑定到了同一棵树;如果 hierarchy 为 0,说明这个子系统没有绑定到任何树,或者它已经绑定到 cgroup v2 的树。
- num_cgroups:该子系统关联的 cgroup 树中有多少个进程组(节点)。比如,memory 的 num_cgroups 是 98,表示内存子系统关联的树中有 98 个进程组。
- enabled:子系统是否开启。1 表示开启,0 表示关闭。
挂载(Mount)定义
- 挂载是将 cgroup 的功能映射到文件系统的某个目录,从而可以通过文件系统的操作来管理 cgroup。
特点
- 每个 cgroup 树需要通过挂载到文件系统来使用。
- 挂载点通常是/sys/fs/cgroup/目录下的子目录。
- 挂载时可以指定绑定的子系统。
示例
# 挂载一个 cgroup 树,绑定 cpu 和 memory 子系统
mount -t cgroup -o cpu,memory cgroup /sys/fs/cgroup/cpu_memory
作用
- 通过文件系统的接口(如cgroup.procs、cgroup.controllers等文件)管理 cgroup。
- 挂载后,可以通过文件操作(如读写文件)来配置资源限制。
Cgroup 文件定义
- cgroup 通过文件系统暴露接口,每个 cgroup 目录下都有一些核心文件,用于配置和监控资源。
常见文件
- cgroup.procs:列出属于该 cgroup 的进程 PID。
- cgroup.controllers:列出该 cgroup 可用的控制器。
- cgroup.subtree_control:启用或禁用子 cgroup 的控制器。
- cgroup.events:显示 cgroup 的状态(如是否被冻结)。
- memory.min和memory.low:设置内存保护。
作用
通过这些文件,用户可以配置资源限制、监控资源使用、迁移进程等。
Cgroup核心交互逻辑
-
一个 hierarchy 可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与零到多个 subsystem 关联。
-
在一颗树里面,会包含 Linux 系统中的所有进程,但每个进程只能属于一个节点(进程组)。
-
系统中可以有很多颗 cgroup 树,每棵树都和不同的 subsystem 关联。
-
一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的 subsystem 关联。
-
目前 Linux 支持 12 - 14 种 subsystem,如果不考虑不与任何 subsystem 关联的情况(systemd 就属于这种情况),Linux 里面最多可以建 12 - 14 颗 cgroup 树,每棵树关联一个 subsystem,当然也可以只建一棵树,然后让这棵树关联所有的 subsystem。
-
当一颗 cgroup 树不和任何 subsystem 关联的时候,意味着这棵树只是将进程进行分组,至于要在分组的基础上做些什么,将由应用程序自己决定,systemd 就是一个这样的例子。
hierarchy(层级结构)与 subsystem(子系统)的关系
- 一个 hierarchy 可以附加多个 subsystem。但是一个 subsystem 只能附加到一个 hierarchy 上。
- Linux 支持 12 - 14 种 subsystem,不考虑特殊情况(如 systemd 这种不与任何 subsystem 关联的情况),Linux 最多可建 12 - 14 颗 cgroup 树,每棵树关联一个 subsystem;也可以只建一棵树并让其关联所有 subsystem。
- 当一个 hierarchy 不与任何 subsystem 关联时,仅用于进程分组,后续操作由应用程序决定,例如 systemd。
hierarchy 与进程(task)的关系
- 系统创建新的 hierarchy 后,系统中所有进程都会加入该 hierarchy 的 cgroup 根节点(默认创建)。
- 对于每个 hierarchy,一个 task 只能存在于其中一个 cgroup 中,即一个 task 不能存在于同一个 hierarchy 的不同 cgroup 中。
- 一个进程可以作为多个 cgroup 的成员,但这些 cgroup 必须在不同的 hierarchy 中,即一个进程可以属于多颗 cgroup 树(不同 hierarchy),且这些树和不同的 subsystem 关联。
subsystem 与进程(task)的关系
- 由于 subsystem 附加在 hierarchy 上,进程存在于 hierarchy 的 cgroup 中,所以进程通过所在的 hierarchy 与相应的 subsystem 产生关联。
进程(task)相关操作
- 一个进程 fork 出子进程时,子进程默认和父进程在同一个 cgroup 中,也可根据需要将其移动到其他 cgroup 中。
- 进程(task)在 fork 自身时创建的子任务(child task)默认与原 task 在同一个 cgroup 中,同样允许被移动到不同的 cgroup 中。
Cgroup的部分特性
操作的原子性和延迟销毁
- 原子性:当你在cgroup.subtree_control文件中指定多个控制器时,这些操作要么全部成功,要么全部失败。这意味着,如果你尝试同时启用cpu和memory控制器,但其中一个失败,那么这两个控制器都不会被启用。
- 延迟销毁:当你销毁一个cgroup的控制器时,这个过程是异步的。这意味着,即使控制器已经被标记为销毁,它可能仍然存在于系统中一段时间,直到所有的引用都被释放。这可能会导致一些延迟引用的问题,即在控制器被销毁后,仍然有进程或代码尝试访问它。
进程和线程的跨cgroup迁移
- 进程的 cgroup 归属和它与其他进程(比如父进程、子进程)之间的关系是没有必然联系的。例如,父进程在 cgroup A 中,但新创建的子进程可以被放置在 cgroup B、cgroup C 等其他任何 cgroup 中,这为灵活地管理进程资源提供了可能。