Cgroup v2 #
Control Groups,linux 内核提供的物理资源隔离机制,实现对 linux 进程或进程组的资源限制、隔离和统计
功能:
- 资源限制
- 优先级控制:不同的组可以有不同的优先级,如 CPU 使用、磁盘 IO
- 审计:计算 group 的资源使用情况
- 控制:挂起一组进程,或者重启一组进程
概念:
- task:任务,对应于系统中运行的一个实体,一般为进程 ==(v2 已废弃)==
- **subsystem:**子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用。比如 CPU 子系统可以控制 CPU 时间,memory 子系统可以控制内存使用量
- cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略
- hierarchy:层级树,一系列 cgroup 组成的树形结构。每个节点都是一个 cgroup,cgroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy
组成 #
cgroup 主要有两个组成部分:
- core - 负责分层组织过程;
- controller - 通常负责沿层次结构分配特定类型的系统资源。每个 cgroup 都有一个
cgroup.controllers
文件,其中列出了所有可供 cgroup 启用的控制器。当在cgroup.subtree_control
中指定多个控制器时,要么全部成功,要么全部失败。在同一个控制器上指定多项操作,那么只有最后一个生效。每个 cgroup 的控制器销毁是异步的,在引用时同样也有着延迟引用的问题;
核心文件 #
文件 | 位置 | 作用 | 补充 | |
---|---|---|---|---|
cgroup.type | 单值 | 非根 cgroup,可读写 | 通过将 “threaded” 写入该文件,将 cgroup 转换为线程 cgroup | - domain: 正常的有效域 cgroup - domain threaded 线程子树根的线程域 cgroup - domain invalid: 无效的 cgroup - threaded: 线程 cgroup,线程子树 |
cgroup.procs | 换行分隔 | 所有 cgroup 都有; 可读写 | 每行列出属于 cgroup 的进程的 pid | |
cgroup.controllers | 空格分隔 | 所有 cgroup 都有; 只读 | 显示 cgroup 可用的所有控制器 | 显示 cgroup 可用的所有控制器 |
cgroup.subtree_control | 空格分隔 | 所有 cgroup 都有; 可读写 | 如果一个控制器在列表中出现不止一次,最后一个有效 | 初始为空 指定多个启用和禁用操作时,要么全部成功,要么全部失败。 "+" 启用;"-" 禁用 |
cgroup.events | 非根 cgroup;只读 | - populated: cgroup 及其子节点中包含活动进程,值为1;无活动进程,值为0 - frozen: cgroup 是否被冻结,冻结值为1;未冻结值为0. |
||
cgroup.freeze | 单值 | 所有 cgroup 都有; 可读写 | 冻结 cgroup 及其所有子节点 cgroup,会将相关的进程关停并且不再运行 | 默认值为0 完成后 events 文件中的 “frozen” 值更新为 1 冻结状态不会影响任何 cgroup 树操作(删除、创建等) |
cgroup.kill | 单值 | 非根 cgroup 上;可读写 | 将 cgroup 及其所有子节点中的 cgroup 杀死(进程会被 SIGKILL 杀掉) | 唯一允许值为1 一般用于将一个 cgroup 树杀掉,防止叶子节点迁移 |
cgroup.max.descendants | 单值 | 可读写 | 最大允许的 cgroup 数量子节点数量 | |
cgroup.max.depth | 单值 | 可读写 | 低于当前节点最大允许的树深度 | |
cgroup.threads | 换行分隔 | 所有 cgroup 都有; 可读写 | 每行列出属于 cgroup 的线程的 TID | |
cgroup.stat | 只读 | - nr_descendants:可见后代的 cgroup 数量 - nr_dying_descendants:被用户删除即将被系统销毁的 cgroup 数量 |
每个进程都属于一个 cgroup,一个进程的所有线程都属于同一个 cgroup。
迁移 #
通过将 PID 写入 cgroup 的 cgroup.procs
实现迁移 。进程的迁移不会影响现有的后代进程所属的 cgroup。
当一个进程 fork 出一个子进程时,该进程就诞生在其父亲进程所属的 cgroup 中。
如果 cgroup 中没有任何子进程或活动进程,可以删除目录,进行销毁(即使存在关联的僵尸进程,也被认为是可以被删除的)。
跨 cgroup 迁移进程代价较高,有状态的资源限制(如内存)不会动态应用于迁移
Cgroups #
树状结构,每个非根 cgroup 含有一个 cgroup.events
文件,其中 populated 字段表示子树是否含有实时进程
所有非根的 cgroup.subtree_control
文件,只能包含在父级中启用的控制器。
cgroup 资源是自上而下分布约束的。只有当资源已经从上游 cgroup 节点分发给下游时,下游的 cgroup 才能进一步分发约束资源。
cgroup.subtree_control
文件只能包含在父节点的 cgroup.subtree_control
文件中启用的控制器内容。
非根 cgroup 只能在没有任何进程时才能将域资源分发给子节点的 cgroup,即 只有不包含任何进程的 cgroup 才能在其 cgroup.subtree_control
文件中启用域控制器,进程总在叶子节点上
挂载 #
- memory_recursiveprot - 递归地将 memory.min 和 memory.low 保护应用于整个子树,无需显式向下传播到叶节点的 cgroup 中,子树内叶子节点可以自由竞争;
- memory_localevents - 只能挂载时设置或者通过从 init 命名空间重新挂载来修改,这是系统范围的选项。只用当前 cgroup 的数据填充 memory.events,如果没有这个选项,默认会计数所有子树;
- nsdelegate - 只能挂载时设置或者通过从 init 命名空间重新挂载来修改,这也是系统范围的选项。它将 cgroup 命名空间视为委托边界,这是两种委派 cgroup 的方式之一;
委派 #
两种方式:
- 设置挂载选项 nsdelegate
- 授权用户对目录及其
cgroup.procs
、cgroup.threads
和cgroup.subtree_control
文件的写访问权限
两种方式的结果相同。一旦被委派,用户就可以在目录下建立子层次结构,所有的资源分配都受父节点的制约。目前,cgroup 对委托子层次结构中的 cgroup 数量或嵌套深度没有任何限制(之后可能会受到明确限制)。
参考资料 #