当前位置: 首页 > news >正文

Kernel中的cgroup2介绍

Control Group v2

原文:

https://docs.kernel.org/admin-guide/cgroup-v2.html

日期:2015年10月

作者:Tejun Heo(Linux内核维护者)

文档说明

本文档是cgroup v2的官方设计规范,完整描述其用户态可见的接口与行为,包括:

•核心架构设计原则

•所有控制器的具体行为规范

•系统级约束与约定

版本要求:所有后续修改必须同步更新本文档。

v1文档位置:Documentation/admin-guide/cgroup-v1/index.rst

Tejun Heo 是 Linux 内核的重要维护者,尤其在控制组(cgroups)领域做出了重大贡献。以下是关于他的主要事迹介绍:

Tejun Heo 是 Linux 内核的重要维护者,尤其在控制组(cgroups)领域做出了重大贡献。以下是关于他的主要事迹介绍:

cgroups 子系统维护者

Tejun Heo 是 Linux 内核中控制组(cgroups)子系统的核心维护者,主导了 cgroups v2 的设计与实现。

他解决了 cgroups v1 的许多设计缺陷(如多层级复杂性、线程粒度和资源竞争问题),推动了更简洁、统一的 cgroups v2 架构。

cgroups v2 的设计

统一层级:cgroups v2 采用单一层级结构,避免了 v1 多层级导致的混乱。

线程粒度支持:通过 cgroup.threads文件支持线程级资源控制,解决了 v1 中进程与线程混合管理的难题。

资源分配模型改进:引入权重(weight)、限制(max)、保护(low)等更清晰的资源分配策略。

其他内核贡献

块设备层(Block Layer):优化 I/O 调度和存储性能。

工作队列(Workqueue):重构了内核异步任务处理机制,提升效率和可维护性。

社区影响

他的工作被容器技术(如 Docker、Kubernetes)广泛依赖,cgroups v2 成为现代 Linux 资源管理的标准。

通过邮件列表和内核峰会积极推动技术讨论,以严谨的工程态度著称。

个人风格

以解决复杂系统问题见长,代码注重长期可维护性。

在 2015 年的文档中,他明确了 cgroups v2 的设计原则,强调简洁性和一致性。

Tejun Heo 的贡献使得 Linux 资源管理更加高效和可靠,为云计算和容器化技术奠定了坚实基础。

介绍

术语

"cgroup" 是 "control group" 的缩写,且永远不大写。单数形式用于指代整个功能特性,也作为限定词使用,如 "cgroup controllers"。当明确指代多个独立的控制组时,使用复数形式 "cgroups"。

什么是cgroup?

cgroup是一种将进程按层级结构组织起来,并以可控且可配置的方式沿层级分配系统资源的机制。cgroup主要由两部分组成——核心部分和控制器。cgroup核心主要负责按层级组织进程。cgroup控制器通常负责沿层级分配特定类型的系统资源,但也存在一些实用控制器,其用途并非资源分配。cgroups形成树状结构,系统中的每个进程都只属于一个cgroup。一个进程的所有线程都属于同一个cgroup。创建时,所有进程都会被放入父进程当时所属的cgroup中。进程可以被迁移到另一个cgroup。进程的迁移不会影响已存在的子进程。在遵循特定结构约束的前提下,可以有针对性地在cgroup上启用或禁用控制器。所有控制器的行为都是层级化的——如果在某个cgroup上启用了控制器,它将影响属于该cgroup及其包含的整个子层级中所有cgroup的进程。当在嵌套的cgroup上启用控制器时,它总是会进一步限制资源分配。层级中靠近根节点设置的资源限制不能被远离根节点的设置覆盖。

基础操作

挂载

与v1不同,cgroup v2只有单一层级结构。可通过以下命令挂载cgroup v2层级:

mount -t cgroup2 none $MOUNT_POINT

cgroup2文件系统的魔数为

0x63677270

(“cgrp”)。所有支持v2且未绑定到v1层级的控制器会自动绑定到v2层级,并显示在根目录下。未在v2层级中活跃使用的控制器可绑定到其他层级。这允许以完全向后兼容的方式混合v2层级与传统的v1多层级结构。

控制器只有在当前层级不再被引用时才能跨层级移动。由于每个cgroup的控制器状态是异步销毁的,且控制器可能存在残留引用,因此在最终卸载前一层级后,控制器可能不会立即出现在v2层级上。同样,控制器需要完全禁用才能从统一层级中移出,且禁用后的控制器可能需要一些时间才能用于其他层级。此外,由于控制器间的依赖关系,可能需要同时禁用其他控制器。

虽然动态移动控制器对开发和手动配置有用,但强烈不建议在生产环境中动态切换v2和其他层级。建议在系统启动后使用控制器之前,就确定层级结构和控制器关联。

在过渡到v2期间,系统管理软件可能仍会在启动时自动挂载v1 cgroup文件系统,从而在手动干预之前劫持所有控制器。为了方便测试和实验,内核参数

cgroup_no_v1=

可以禁用v1控制器,使其始终在v2中可用。

cgroup v2目前支持以下挂载选项:

’nsdelegate

将cgroup命名空间视为委托边界。此选项是系统范围的,只能在挂载时设置或通过init命名空间重新挂载修改。非init命名空间的挂载会忽略此选项。详情请参阅"委托"部分。

favordynmods

以增加fork和exit等热路径操作为代价,减少动态cgroup修改(如任务迁移和控制器开关)的延迟。创建cgroup、启用控制器并通过

CLONE_INTO_CGROUP

初始化的静态使用模式不受此选项影响。

memory_localevents

仅填充当前cgroup的

memory.events

数据,不包括子树。这是传统行为,默认行为(无此选项)是包含子树计数。此选项是系统范围的,只能在挂载时设置或通过init命名空间重新挂载修改。非init命名空间的挂载会忽略此选项。

memory_recursiveprot

递归地将

memory.min

memory.low

保护应用于整个子树,无需显式向下传播到叶子cgroup。这允许保护整个子树彼此不受干扰,同时保留这些子树内部的自由竞争。这本应是默认行为,但作为挂载选项以避免破坏依赖原始语义的设置(例如在更高树级别指定虚假的高"绕过"保护值)。

memory_hugetlb_accounting

将HugeTLB内存使用计入cgroup的总体内存使用(用于统计报告和内存保护)。这是一种新行为,可能影响现有设置,因此必须通过此挂载选项显式启用。

需要注意以下几点:

内存控制器不涉及HugeTLB池管理。预分配的池不属于任何人。具体来说,当新HugeTLB folio分配到池时,从内存控制器的角度看不会被计入。只有在实际使用时(例如在缺页时)才会计入cgroup。主机内存超配管理在配置硬限制时需考虑这一点。通常,HugeTLB池管理应通过其他机制(如HugeTLB控制器)完成。

未能将HugeTLB folio计入内存控制器会导致

SIGBUS

。即使HugeTLB池仍有可用页面(但达到cgroup限制且回收尝试失败),也可能发生这种情况。

将HugeTLB内存计入内存控制器会影响内存保护和回收动态。任何用户空间调优(如调整low、min限制)都需要考虑这一点。

未选择此选项时使用的HugeTLB页面不会被内存控制器跟踪(即使后续重新挂载cgroup v2)。

pids_localevents

此选项恢复

pids.events:max

的v1类行为,即仅统计本地(cgroup内部)的fork失败。无此选项时,

pids.events.max

表示cgroup子树中的任何

pids.max

强制执行。

线程管理

cgroup v2支持对部分控制器实现线程粒度控制,以满足跨进程线程组的层级资源分配需求。默认情况下,进程的所有线程属于同一cgroup(该cgroup也作为非线程专属资源消耗的宿主域)。线程模式允许线程分散在子树中,同时为它们维护公共资源域。支持线程模式的控制器称为

线程化控制器

(threaded controllers),不支持的称为

域控制器

(domain controllers)。

将cgroup标记为线程化会使其作为线程化cgroup加入父cgroup的资源域。父cgroup可能是另一个线程化cgroup(其资源域在层级中更靠上)。线程化子树的根(即最近的未线程化祖先)称为

线程化域

线程根

,作为整个子树的资源域。

在线程化子树中:

进程的线程可放入不同cgroup

不受"无内部进程"约束限制(非叶子cgroup即使不含线程也可启用线程化控制器)

线程化域cgroup承载子树的所有域资源消耗,因此无论是否包含进程都被视为具有内部资源消耗,且不能拥有非线程化的已填充子cgroup。根cgroup不受"无内部进程"约束,故可同时作为线程化域和域cgroup的父级。

cgroup的当前操作模式通过

cgroup.type

文件显示,包含三种状态:

普通域(domain)

作为线程化子树根的域(domain threaded)

线程化cgroup(threaded)

新创建的cgroup默认为域cgroup,可通过写入

threaded

转换为线程化(单向操作):

echo threaded > cgroup.type

转换需满足以下条件:

父cgroup必须是有效的(线程化)域或线程化cgroup

若父cgroup是未线程化域,则不得启用任何域控制器或包含已填充的域子cgroup(根cgroup除外)

拓扑无效的cgroup(如新创建的域cgroup未连接到可承载子域的父级)会显示

domain (invalid)

状态,相关操作将返回

EOPNOTSUPP

错误。

域cgroup在以下情况转为线程化域:

子cgroup变为线程化

启用

cgroup.subtree_control

中的线程化控制器且该cgroup包含进程

cgroup.threads

文件列出cgroup中所有线程ID(格式与行为同

cgroup.procs

,但操作粒度是线程级)。写入

cgroup.threads

仅能在同一线程化域内移动线程。

线程化域cgroup作为整个子树的资源域:

cgroup.procs

包含子树中所有进程PID(子树内部不可读)

子树中任意位置写入

cgroup.procs

可迁移目标进程的所有线程

仅线程化控制器可在线程化子树中启用。启用后,控制器仅统计和控制与该cgroup及其后代中线程相关的资源消耗,非线程专属消耗归属于线程化域cgroup。

当前支持的线程化控制器包括:

cpu

cpuset

perf_event

pids

填充状态通知

每个非根cgroup的

cgroup.events

文件包含

populated

字段:

0

:该cgroup及其后代无存活进程

1

:存在存活进程

当值变化时触发

poll

[id]notify

事件。例如:可在某子树的所有进程退出后触发清理操作。填充状态更新与通知是递归的。

示例(括号内数字表示各cgroup的进程数):

A(4) - B(0) - C(1)            \ D(0)

此时A、B、C的

populated

为1,D为0。当C的进程退出后,B和C的

populated

会变为0,并生成文件修改事件。

控制器管理

可用性

当控制器满足以下条件时,会在cgroup中可用:

内核支持(已编译且未禁用)

未绑定到v1层级

列于

cgroup.controllers

文件中

此时控制器接口文件会暴露在该cgroup目录下,允许观察或控制目标资源的分配。

启用与禁用

每个cgroup的

cgroup.controllers

文件列出所有可启用的控制器:

cd /sys/fs/cgroup # cat cgroup.controllers cpu io memory

默认不启用任何控制器。通过写入

cgroup.subtree_control

文件启用/禁用控制器:

echo “+cpu +memory -io” > cgroup.subtree_control

规则说明:

cgroup.controllers

列出的控制器可操作

多操作同时指定时,全部成功或全部失败

对同一控制器的多次操作,最后一次生效

层级控制逻辑

启用控制器的cgroup将控制该资源在其直接子cgroup间的分配。例如以下层级(括号内为启用的控制器):

A(cpu,memory) - B(memory) - C()                          \ D()

A启用

cpu

memory

,控制B的CPU和内存分配

B仅启用

memory

,故C和D自由竞争CPU,但内存分配受B控制

控制器接口文件(非

cgroup.

前缀)由父cgroup创建/管理:

在B启用

cpu

会在C/D创建

cpu.

前缀文件

在B禁用

memory

会移除C/D的

memory.

前缀文件

约束条件

自上而下约束

资源分配遵循自上而下原则:

非根cgroup只能启用父cgroup已启用的控制器

若子cgroup启用了某控制器,父cgroup无法禁用该控制器

无内部进程约束

非根cgroup必须满足以下条件才能启用域控制器:

自身不包含任何进程

所有进程必须迁移到子cgroup

此约束保证:

启用域控制器的层级中,进程仅存在于叶子节点

避免子cgroup与父cgroup内部进程竞争资源

根cgroup不受此限制,因其包含:

未关联到其他cgroup的进程

匿名资源消耗(由各控制器特殊处理)

特殊说明

若cgroup未启用任何控制器:

可自由创建子cgroup并迁移进程

需先完成进程迁移再启用控制器

委托机制

委托模型

cgroup可通过两种方式委托:

特权降级

:授予用户对目录及其

cgroup.procs

cgroup.threads

cgroup.subtree_control

文件的写入权限

命名空间自动委托

:若设置

nsdelegate

挂载选项,在创建cgroup命名空间时自动委托

由于资源控制接口文件控制父级资源分配,委托方不应获得其写入权限:

方法1

:通过权限控制禁止访问这些文件

方法2

:通过挂载命名空间隐藏命名空间外的文件,内核会拒绝从cgroup命名空间内部对命名空间根目录下所有文件的写入(

/sys/kernel/cgroup/delegate

列表中的文件除外,包括

cgroup.procs

cgroup.threads

cgroup.subtree_control

等)

两种委托的最终效果相同:用户可在委托目录下构建子层级、自由组织进程并分配从父级接收的资源。所有控制器的限制与设置均为层级化,确保子层级内的操作无法突破父级施加的资源约束。

当前cgroup对委托子层级的cgroup数量和嵌套深度无限制,但未来可能显式限制。

委托隔离性

委托的子层级具有隔离性——委托方无法将进程移入或移出该子层级。

对降级用户的隔离实现

非root用户迁移进程时需满足以下条件:

对目标

cgroup.procs

文件有写入权限

对源和目标cgroup共同祖先的

cgroup.procs

文件有写入权限

这两项约束确保委托方可在子层级内自由迁移进程,但无法从外部拉入或向外部推出进程。

示例

假设用户U0被委托了C0和C1,并创建了子cgroup:


若用户U0尝试将C10中的进程PID写入C00/cgroup.procs:虽对目标文件有写入权限但共同祖先位于委托点之上,用户U0无其cgroup.procs的写入权限操作将被拒绝(返回-EACCES)对命名空间的隔离实现:要求源和目标cgroup均能从执行迁移操作的进程所在命名空间访问。若任一方不可达,迁移将被拒绝(返回-ENOENT)。使用指南一次性组织与持续控制进程跨cgroup迁移是相对昂贵的操作,且状态性资源(如内存)不会随进程迁移。这是明确的设计选择——迁移操作与热路径执行在同步成本上存在固有权衡。建议做法:在启动时根据系统逻辑和资源结构将工作负载分配到对应cgroup动态调整资源分配应通过修改控制器的接口文件实现避免频繁迁移进程作为资源限制调整手段避免命名冲突cgroup及其子cgroup的接口文件共享同一目录,可能因创建子cgroup导致名称冲突。命名规范:核心接口文件前缀为cgroup.(如cgroup.procs)控制器接口文件前缀为控制器名.(如cpu.weight)控制器名称由小写字母和_组成,且不以_开头接口文件不会以常见工作负载分类术语开头或结尾(如job、service、slice、unit、workload)注意事项:cgroup不主动防止命名冲突用户需自行确保子cgroup命名不与接口文件重复资源分配模型cgroup控制器根据资源类型和使用场景实现多种分配方案,主要模型如下:权重分配(Weights)机制:父级资源按子cgroup的权重比例分配仅当前能使用资源的子cgroup参与分配(工作保留性)适用于无状态资源(如CPU时间片)参数范围:权重值:[1, 10000](默认值100)支持双向对称比例调整,同时保持直观数值范围特点:只要权重值在范围内,所有配置组合均有效无需拒绝配置变更或进程迁移操作示例:cpu.weight—— 按比例分配CPU时间给活跃子cgroup限额分配(Limits)机制:子cgroup的消耗量不得超过设定限额支持超额分配(子cgroup限额总和可超过父级资源总量)参数范围:限额值:[0, max](默认值max表示无限制)特点:因支持超额分配,所有配置组合均有效无需拒绝配置变更或进程迁移操作示例:io.max—— 限制cgroup在IO设备上的最大带宽(BPS)或IOPS资源保护与分配模型资源保护(Protections)机制:当所有祖先cgroup的资源使用量低于其保护阈值时,当前cgroup的资源消耗将受到保护(不超过配置值)保护机制可以是硬性保证或尽力而为的软性边界支持超额配置(子cgroup的保护总量可超过父级可用资源,实际仅保护父级可用部分)参数范围:保护值:[0, max](默认值0,表示无保护)特点:因支持超额配置,所有配置组合均有效不会因保护配置拒绝进程迁移或参数修改示例:memory.low—— 对内存使用提供尽力而为的软性保护独占分配(Allocations)机制:为cgroup独占分配固定数量的有限资源(如实时CPU时间片)禁止超额配置(子cgroup分配总量不得超过父级可用资源)参数范围:分配值:[0, max](默认值0,表示不分配资源)特点:无效配置(如子级分配总和超限)会被拒绝若资源为进程执行的必要条件(如实时CPU),可能拒绝迁移请求示例:cpu.rt.max—— 硬性分配实时CPU时间片接口文件格式规范cgroup接口文件需遵循以下格式标准(按优先级排序):1. 换行分隔值适用场景:单次写入仅支持单个值读取示例:VAL0   VAL1   ...写入规则:每次写入必须为完整单行(如echo "VAL0" > file)2. 空格分隔值适用场景:只读文件或支持单次写入多值示例:VAL0 VAL1 ...写入规则:支持单次写入多个空格分隔值(如echo "VAL0 VAL1" > file)3. 扁平键值对格式:KEY0 VAL0   KEY1 VAL1   ...写入规则:每次仅允许写入单个键的值(如echo "KEY0 VAL0" > file)键名区分大小写4. 嵌套键值对格式:复制KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01...   KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11...   ...写入规则:每次仅允许操作单个主键(如echo "KEY0 SUB_KEY0=VAL00" > file)子键可无序/省略(如仅更新部分子键值)通用规则1.读写一致性:可写文件的写入格式需与读取格式匹配(允许省略尾部字段)2.快捷操作:控制器可为高频场景提供简化写入语法(如省略默认键名)3.错误处理:无效格式写入必须返回EINVAL错误cgroup接口规范基本原则1.单一功能单一文件:每个功能配置应当集中在一个文件中2.根cgroup豁免:根cgroup不参与资源控制,不应包含资源控制接口文件3.时间单位规范:默认使用微秒(μs),若使用其他单位必须明确标注单位后缀数值格式标准1.百分比数值:使用包含至少两位小数的百分数格式(如13.40)2.权重配置:•接口文件必须命名为"weight"•取值范围[1, 10000]•默认值设为100(对应100%)3.资源限制配置:•绝对资源保证/限制:使用"min"/"max"命名•尽力而为资源保证/限制:使用"low"/"high"命名•无限值统一使用"max"表示默认值配置规范1.默认值标记:•必须使用"default"作为键名•必须作为文件中的第一条记录2.默认值修改:•支持两种格式:echo"default 125"> fileecho125 > file3.覆盖规则:•设置特定覆盖值:echo"8:16 170"> file•清除特定覆盖值(恢复默认):echo"8:0 default"> file事件通知机制1.事件文件:•对非高频事件,应当创建"events"接口文件•文件内容为事件键值对列表2.通知触发:•当可通知事件发生时•必须生成文件修改事件示例说明# 初始状态 # cat cgroup-example-interface-file default 150 8:0 300  # 修改默认值 echo 125 > file # 或 echo "default 125" > file  # 设置设备8:16的覆盖值 echo "8:16 170" > file  # 清除设备8:0的覆盖值 echo "8:0 default" > file  # 最终状态 # cat cgroup-example-interface-file default 125 8:16 170核心接口文件所有 cgroup 核心文件均以 cgroup.为前缀。1. cgroup.type类型:读写单值文件(仅非根 cgroup 存在)读取值:"domain":普通有效的域 cgroup"domain threaded":作为线程化子树根的域 cgroup"domain invalid":处于无效状态的 cgroup(无法填充或启用控制器,但可转为线程化 cgroup)"threaded":线程化 cgroup(属于线程化子树成员)写入操作:写入 "threaded"可将 cgroup 转为线程化(单向操作)。例如我的手机是domain2. cgroup.procs类型:读写换行分隔值文件(所有 cgroup 存在)读取:列出属于该 cgroup 的所有进程 PID(无序,可能重复)。写入:写入 PID 可迁移对应进程至该 cgroup。需满足:对目标 cgroup.procs有写入权限对源和目标 cgroup 的共同祖先 cgroup.procs有写入权限线程化 cgroup:读取返回 EOPNOTSUPP(因所有进程属于线程根),写入会迁移进程的所有线程。3. cgroup.threads类型:读写换行分隔值文件(所有 cgroup 存在)读取:列出属于该 cgroup 的所有线程 TID(无序,可能重复)。写入:写入 TID 可迁移对应线程至该 cgroup。需满足:对目标 cgroup.threads有写入权限源 cgroup 与目标 cgroup 在同一资源域对共同祖先 cgroup.procs有写入权限4. cgroup.controllers类型:只读空格分隔值文件(所有 cgroup 存在)内容:列出该 cgroup 可用的所有控制器(无序)。实测如下:5. cgroup.subtree_control类型:读写空格分隔值文件(初始为空)读取:列出已启用控制器(控制资源分配到子 cgroup)。写入:以 +(启用)或 -(禁用)前缀操作控制器(如 "+cpu -memory")。多次操作同一控制器时,最后一次生效。6. cgroup.events类型:只读扁平键值文件(非根 cgroup 存在)键值:populated:1(含存活进程或其子 cgroup 含进程)或 0frozen:1(已冻结)或 0事件:值变更时触发文件修改事件。7. cgroup.max.descendants类型:读写单值文件(默认 "max")功能:限制子 cgroup 的最大数量(超限时创建失败)。8. cgroup.max.depth类型:读写单值文件(默认 "max")功能:限制当前 cgroup 下的最大嵌套深度(超限时创建子 cgroup 失败)。9. cgroup.stat类型:只读扁平键值文件键值:nr_descendants:可见子 cgroup 总数nr_dying_descendants:待销毁子 cgroup 总数(删除后进入此状态)nr_subsys_:当前及子 cgroup 的活动子系统数(如 memory)nr_dying_subsys_:当前及子 cgroup 的待销毁子系统数10. cgroup.freeze类型:读写单值文件(非根 cgroup 存在,默认 "0")操作:写入 "1":冻结该 cgroup 及所有子 cgroup(进程停止运行)写入 "0":解冻特性:冻结状态传播:祖先 cgroup 冻结会导致后代冻结进程可被终止或迁移(迁入停止,迁出恢复运行)不影响 cgroup 树操作(可删除冻结的空 cgroup 或创建子 cgroup)11. cgroup.kill类型:只写单值文件(非根 cgroup 存在)操作:写入 "1"杀死该 cgroup 及所有子 cgroup 中的进程(发送 SIGKILL)。线程化 cgroup:写入返回 EOPNOTSUPP(因需操作整个线程组)。12. cgroup.pressure类型:读写单值文件(默认 "1")操作:"0":禁用 PSI(Pressure Stall Information)统计"1":启用 PSI 统计特性:非层级化(不影响后代 cgroup)。我的android 15 手机没有该文件节点13. irq.pressure类型:读写嵌套键值文件功能:显示 IRQ/SOFTIRQ 的压力停滞信息(详见 Documentation/accounting/psi.rst)。我的android 15 手机没有该文件节点CPU控制器CPU控制器负责调节CPU时间片的分配。该控制器为普通调度策略实现了权重和绝对带宽限制模型,并为实时调度策略实现了绝对带宽分配模型。在上述所有模型中,时间片分配仅基于时间维度,不考虑任务执行频率。(可选的)利用率钳制功能允许向schedutilCPU频率调节器提示:最低期望频率(CPU必须始终提供的频率)最高期望频率(CPU不应超过的频率)警告当前cgroup v2的CPU控制器不支持实时进程的带宽控制。对于启用CONFIG_RT_GROUP_SCHED选项的内核(用于实时进程的组调度),必须满足以下条件才能启用CPU控制器:所有实时进程必须位于根cgroup系统管理软件可能在启动过程中已将实时进程放入非根cgroup,需手动迁移至根cgroup若禁用CONFIG_RT_GROUP_SCHED,此限制不适用,部分接口文件可影响或统计实时进程。仅CPU控制器受此配置影响,其他控制器对实时进程的资源控制不受限制。CPU接口文件进程与CPU控制器的交互取决于其调度策略和底层调度器。CPU控制器将进程分为三类:1.公平调度类(fair-class)进程2.支持cgroup_set_weight回调的BPF调度器进程3.其他进程:包括SCHED_FIFO、SCHED_RR、SCHED_DEADLINE以及不支持cgroup_set_weight回调的BPF调度器进程(详见Documentation/scheduler/sched-ext.rst)文件说明1. cpu.stat类型:只读扁平键值文件(无论控制器是否启用均存在)全局统计项(涵盖所有进程):•usage_usec:总CPU使用时间•user_usec:用户态CPU时间•system_usec:内核态CPU时间公平调度类统计项(仅控制器启用时显示):•nr_periods:调度周期数•nr_throttled:被限制次数•throttled_usec:被限制总时间•nr_bursts:突发使用次数•burst_usec:突发使用总时间2. cpu.weight类型:读写单值文件(非根cgroup,默认值100)取值范围:[1, 10000](若cpu.idle=1则显示为0)作用范围:仅影响公平调度类和支持cgroup_set_weight回调的BPF调度器进程3. cpu.weight.nice类型:读写单值文件(非根cgroup,默认值0)取值范围:[-20, 19](对应nice值)功能:通过nice值读写权重(范围更小、粒度更粗)作用范围:同cpu.weight4. cpu.max类型:读写双值文件(非根cgroup,默认值max 100000)格式:$MAX $PERIOD(如50000 100000表示每100ms分配50ms)•max表示无限制•单数值写入时仅更新$MAX作用范围:仅影响公平调度类进程5. cpu.max.burst类型:读写单值文件(非根cgroup,默认值0)取值范围:[0, $MAX]($MAX来自cpu.max)作用范围:仅影响公平调度类进程6. cpu.pressure类型:读写嵌套键值文件功能:显示CPU压力停滞信息(详见Documentation/accounting/psi.rst)作用范围:统计所有进程7. cpu.uclamp.min类型:读写单值文件(非根cgroup,默认值0)格式:百分比有理数(如12.34表示12.34%)功能:设置最低利用率限制(钳制任务级设置,含实时进程)约束:实际值不超过cpu.uclamp.max作用范围:影响所有进程8. cpu.uclamp.max类型:读写单值文件(非根cgroup,默认值max)格式:百分比有理数(如98.76表示98.76%)功能:设置最高利用率限制(钳制任务级设置,含实时进程)作用范围:影响所有进程9. cpu.idle类型:读写单值文件(非根cgroup,默认值0)功能:设为1时,cgroup内进程视为SCHED_IDLE优先级(相对同级cgroup极低优先级)作用范围:仅影响公平调度类进程内存控制器内存控制器负责内存资源的分配与管理。由于内存具有状态性且与回收压力紧密相关,其分配模型较为复杂。当前已跟踪以下主要内存使用类型:用户态内存(页面缓存和匿名内存)内核数据结构(如目录项和索引节点)TCP套接字缓冲区接口文件说明所有内存数值以字节为单位,写入值若非页大小(PAGE_SIZE)倍数,读取时可能向上取整。1. 基础控制文件文件类型默认值功能说明memory.current只读单值-当前cgroup及其后代的内存使用总量memory.min读写单值0硬保护:内存使用低于此值时绝不回收;若系统无可用内存则触发OOMmemory.low读写单值0软保护:内存使用低于此值时仅当无其他可回收内存才回收memory.high读写单值max限流阈值:超限时进程被抑制并面临高回收压力(不触发OOM)memory.max读写单值max硬限制:超限且无法回收时触发OOMmemory.reclaim只写嵌套键-主动触发内存回收(如echo "1G" > memory.reclaim)memory.peak读写单值-记录cgroup历史峰值内存使用,写入非空字符串可重置计数memory.oom.group读写单值0设为1时OOM killer将cgroup视为不可分割单元(全杀或全留)2. 事件监控文件文件类型监控内容memory.events只读扁平键层级化事件统计(如low/high/oom触发次数)memory.events.local只读扁平键仅本地cgroup事件统计(非层级化)memory.stat只读扁平键详细内存分类统计(含anon/file/slab等20+项)memory.numa_stat只读嵌套键按NUMA节点分列的内存使用统计3. Swap控制文件文件类型默认值功能说明memory.swap.current只读单值-当前swap使用总量memory.swap.high读写单值maxswap限流阈值(超限后抑制新分配)memory.swap.max读写单值maxswap硬限制(超限后禁止swap-out)memory.swap.events只读扁平键-swap事件统计(如high/max触发次数)4. Zswap控制文件文件类型默认值功能说明memory.zswap.current只读单值-zswap压缩后端内存使用量memory.zswap.max读写单值maxzswap硬限制(超限后拒绝新存储)memory.zswap.writeback读写单值1设为0时禁用所有swap-out(含zswap回写)5. 压力监控文件文件类型功能说明memory.pressure只读嵌套键内存压力停滞信息(详见PSI文档)关键机制说明1.保护与限制层级•memory.min和memory.low受祖先cgroup设置约束•若子cgroup保护值总和超父级可用内存,按实际使用比例分配保护额度2.Swap管理特性•memory.swap.high用于优雅限流,memory.swap.max用于强制限制•降低swap限制后,现有swap条目逐步回收,可能暂时超限3.内存所有权规则•内存归属创建它的cgroup,进程迁移不转移已分配内存•跨cgroup共享的内存可能被任意cgroup计费,但倾向于留在有足够配额的cgroup4.NUMA亲和性•memory.numa_stat提供NUMA节点粒度统计,辅助结合CPU分配优化性能使用建议1.主要控制策略•用memory.high实现弹性限制,依赖全局内存压力自动调节•超限时管理代理可动态调整配额或终止任务2.内存压力评估•当前缺乏内置压力监测机制,需结合memory.stat和memory.pressure人工判断3.性能优化•对高频访问的共享文件,使用posix_fadvise(POSIX_FADV_DONTNEED)明确释放所有权•监控workingset_*统计项识别活跃工作集4.OOM处理•对关键服务设置memory.oom.group=1避免部分杀死•豁免进程需设置oom_score_adj=-1000IO控制器该控制器负责IO资源的分配,支持权重比例分配和绝对带宽/IOPS限制两种模式(权重模式需使用cfq-iosched调度器,且不适用于blk-mq设备)。接口文件说明1. 统计监控文件文件类型功能说明io.stat只读嵌套键按设备号($MAJ:$MIN)统计:• rbytes/wbytes:读写字节数• rios/wios:读写IO次数• dbytes/dios:丢弃操作统计示例输出:8:16 rbytes=1459200 wbytes=314773504 rios=192 wios=353 dbytes=0 dios=0   8:0 rbytes=90430464 wbytes=299008000 rios=8950 wios=1252 dbytes=50331648 dios=30212. 权重控制文件文件类型默认值功能说明io.weight读写扁平键default 100• 首行为默认权重(1-10000)• 后续行可覆盖特定设备权重(如8:16 200)• 写default恢复默认示例操作:echo "default 150" > io.weight          # 修改默认权重 echo "8:0 50" >> io.weight              # 设置设备8:0的权重 echo "8:0 default" >> io.weight         # 恢复设备8:0的默认权重3. 带宽限制文件文件类型功能说明io.max读写嵌套键按设备号限制:• rbps/wbps:读写带宽(字节/秒)• riops/wiops:读写IOPS示例操作:echo "8:16 rbps=2097152 wiops=120" > io.max   # 限制设备8:16读带宽2MB/s、写IOPS 120 echo "8:16 wiops=max" > io.max                 # 取消写IOPS限制输出示例:8:16 rbps=2097152 wbps=max riops=max wiops=1204. 高级控制文件(仅根cgroup)文件类型功能说明io.cost.qos读写嵌套键配置IO成本模型的QoS:• enable=1启用控制• rpct/wpct:延迟百分位(0-100)• rlat/wlat:延迟阈值(微秒)• min/max:缩放比例(1-10000)io.cost.model读写嵌套键配置成本模型参数:• model=linear:线性模型• `[rQoS配置示例:echo "8:16 enable=1 ctrl=auto rpct=95 rlat=75000 wpct=95 wlat=150000 min=50 max=150" > io.cost.qos5. 压力监控文件文件类型功能说明io.pressure只读嵌套键显示IO压力停滞信息(详见PSI文档)关键特性1.权重分配•权重值范围[1, 10000],默认100•高权重cgroup获得更多IO时间片2.限制模式•支持按设备设置带宽(BPS)和IOPS硬限制•临时突发允许超限,但持续超限会触发延迟3.动态调节•io.cost.qos可基于延迟百分位动态调整IO速率•ctrl=auto时内核自动优化参数,ctrl=user时手动配置4.成本模型•线性模型(model=linear)计算IO基础成本•工具iocost_coef_gen.py可生成设备特定系数使用建议1.关键服务保障•为高优先级cgroup分配更高权重(如io.weight=5000)•对延迟敏感服务配置io.cost.qos的严格延迟阈值2.带宽限制场景•限制备份服务的写带宽:echo "8:0 wbps=104857600" > io.max(100MB/s)•限制日志服务的IOPS:echo "8:16 wiops=500" > io.max3.监控与调优•通过io.stat实时监控设备级IO负载•结合io.pressure识别IO瓶颈回写机制(Writeback)回写机制负责将脏页(通过缓冲写入或共享内存映射修改)异步写入底层文件系统。它位于内存域和IO域之间,通过平衡脏页生成和回写IO来调节脏内存比例。cgroup回写控制内存控制器:定义脏内存比例的计算和维护范围(内存域)。IO控制器:定义执行脏页回写的IO域。限制规则:同时检查全局和cgroup级的脏内存状态,并应用更严格的限制。文件系统支持当前支持cgroup回写的文件系统:ext2、ext4、btrfs、f2fs、xfs其他文件系统:所有回写IO归属于根cgroup内存与回写的所有权差异维度内存所有权回写所有权跟踪粒度按页(page)按inode(索引节点)归属变更首次使用时分配,释放前不变动态调整(若某cgroup长期占多数脏页)外源页(Foreign Pages)定义:内存归属于某cgroup,但其inode归属于另一cgroup的脏页。处理:若某cgroup长期占多数脏页,回写机制会将inode所有权切换至该cgroup。限制场景单inode多cgroup写入:可能导致IO归属错误(不建议使用此模式)。系统参数与cgroup回写sysctl参数cgroup回写行为vm.dirty_background_ratio按比例限制脏内存,上限受内存控制器和系统空闲内存约束vm.dirty_ratio同上vm.dirty_background_bytes转换为相对于总可用内存的比例,行为同vm.dirty_background_ratiovm.dirty_bytes同上IO延迟控制(IO Latency)功能为cgroup设置延迟目标(io.latency),若平均延迟超限,则限制低优先级同级cgroup的IO。层级约束:仅同级cgroup相互影响(如右图A/B/C相互制约,D/F相互制约,G不受影响)。限制机制1.队列深度限制:从无限制逐步降至1个IO。2.人工延迟注入:对无法直接限制的IO(如交换、元数据IO),通过use_delay和delay字段统计延迟(单次最多1秒)。接口文件文件功能io.latency设置延迟目标(微秒),如8:16 target=75000io.stat新增统计项:• depth:当前队列深度• avg_lat:指数移动平均延迟IO优先级(IO Priority)策略属性(io.prio.class)策略行为数值no-change不修改IO优先级0promote-to-rt非RT请求升为RT类,优先级设为4;RT类保持不变1restrict-to-be无类/RT类请求降为BE类,优先级设为0;IDLE类保持不变2idle所有请求设为IDLE类(最低优先级)3优先级类数值类数值IOPRIO_CLASS_NONE0IOPRIO_CLASS_RT1IOPRIO_CLASS_BE2IOPRIO_CLASS_IDLE3请求优先级设置逻辑1.若策略为promote-to-rt:设为RT类,优先级=4。2.否则:取策略数值与请求当前类数值的较大者作为最终类。关键建议1.避免多cgroup并发写入同一inode,以防IO归属错误。2.IO延迟调优:•初始值设为高于设备预期延迟•根据io.stat的avg_lat调整,最终值比观测值高10-15%3.优先级策略:对延迟敏感任务使用promote-to-rt,后台任务用idle。PID控制器PID控制器用于限制cgroup内可创建的进程/线程数量。当达到指定限制时,将阻止新的fork()或clone()操作。接口文件说明文件类型默认值功能说明pids.max读写单值max硬性限制cgroup及其子cgroup中的进程总数pids.current只读单值-当前cgroup及其子cgroup中的进程总数pids.peak只读单值-记录cgroup及其子cgroup中进程数量的历史峰值pids.events只读扁平键-层级化事件统计:• max:达到pids.max限制的次数pids.events.local只读扁平键-本地事件统计(不包含子cgroup事件)关键特性1.限制粒度•统计单位是内核级线程ID(TID),即每个线程计为1个进程•包含cgroup自身及其所有子cgroup的进程总数2.限制执行•通过fork()/clone()创建新进程时,若导致pids.current > pids.max则返回-EAGAIN•但通过其他方式(如直接附加进程)可能使pids.current暂时超限3.典型应用场景•防止fork炸弹耗尽系统PID•限制容器内进程数量(作为内存控制的补充)示例操作# 设置进程数限制为100 echo 100 > pids.max  # 查看当前进程数 cat pids.current  # 监控突破限制事件 tail -f pids.events注意:该控制器不限制通过exec替换进程的操作,仅限制新进程的创建。Cpuset控制器该控制器用于将任务限制在指定的CPU和内存节点上运行,尤其适用于NUMA系统以减少跨节点内存访问和争用。接口文件说明1. CPU分配文件类型功能说明cpuset.cpus读写多值文件请求该cgroup使用的CPU列表(如0-4,6,8-10),空值表示继承父cgroup或使用所有CPUcpuset.cpus.effective只读多值文件实际可用的在线CPU列表(受父cgroup约束)cpuset.cpus.exclusive读写多值文件独占CPU列表(用于创建分区,需满足与兄弟cgroup的互斥规则)cpuset.cpus.exclusive.effective只读多值文件实际可用的独占CPU列表(用于分区)cpuset.cpus.isolated只读多值文件(仅根cgroup)显示所有隔离分区中的CPU2. 内存节点分配文件类型功能说明cpuset.mems读写多值文件请求该cgroup使用的内存节点列表(如0-1,3),空值表示继承父cgroup或使用所有节点cpuset.mems.effective只读多值文件实际可用的在线内存节点列表(受父cgroup约束)3. 分区控制文件类型功能说明cpuset.cpus.partition读写单值文件设置分区状态:• member:普通成员• root:分区根• isolated:无负载均衡的隔离分区关键概念1. 层级约束子cgroup的CPU/内存节点必须是父cgroup的子集根cgroup默认包含所有CPU和内存节点2. 分区(Partition)分区根:独占一组CPU,外部cgroup无法使用这些CPU本地分区:父cgroup也是分区根(cpuset.cpus.exclusive可隐式继承)远程分区:父cgroup非分区根(需显式设置cpuset.cpus.exclusive)隔离分区:CPU禁用调度器负载均衡和工作队列(需手动绑定任务到各CPU)3. 独占CPU规则同一父cgroup下,兄弟cgroup的独占CPU不能重叠独占CPU分配需通过cpuset.cpus.exclusive显式声明4. 状态有效性有效分区根需满足:•父cgroup是有效分区根(本地分区)•cpuset.cpus.exclusive.effective非空(允许离线CPU)•若无任务关联,cpuset.cpus.effective可为空5. 动态事件CPU/内存热插拔或配置变更可能导致分区状态失效通过poll/inotify监控cpuset.cpus.partition状态变化操作示例1. 设置CPU和内存节点# 分配CPU 0-3和内存节点0-1 echo "0-3" > cpuset.cpus echo "0-1" > cpuset.mems2. 创建独占分区# 声明独占CPU 2-3 echo "2-3" > cpuset.cpus.exclusive # 设置为分区根 echo "root" > cpuset.cpus.partition3. 创建隔离分区# 声明独占CPU 4-5并隔离 echo "4-5" > cpuset.cpus.exclusive echo "isolated" > cpuset.cpus.partition4. 查看实际分配cat cpuset.cpus.effective      # 实际可用CPU cat cpuset.mems.effective      # 实际可用内存节点 cat cpuset.cpus.partition      # 当前分区状态注意事项1.内存迁移成本:修改cpuset.mems会迁移任务内存,建议在任务启动前设置2.分区切换风险:将分区根改为member会导致子分区失效3.隔离CPU:通过内核启动参数isolcpus预配置的CPU需放入隔离分区使用设备控制器(Device Controller)该控制器通过BPF程序管理设备文件的访问权限(包括mknod创建设备文件及读写现有设备文件)。实现机制无接口文件:完全基于cgroup BPF实现控制方式:1.编写类型为BPF_PROG_TYPE_CGROUP_DEVICE的BPF程序2.通过BPF_CGROUP_DEVICE标志附加到目标cgroup执行逻辑:•程序接收bpf_cgroup_dev_ctx结构体参数(描述设备访问类型及设备号)•返回0拒绝访问(-EPERM),非0允许访问示例参考内核源码中的示例程序:tools/testing/selftests/bpf/progs/dev_cgroup.cRDMA控制器该控制器用于分配和统计RDMA(远程直接内存访问)资源。接口文件文件类型功能说明rdma.max读写嵌套键设置RDMA设备资源硬限制:• hca_handle:最大HCA句柄数• hca_object:最大HCA对象数rdma.current只读嵌套键显示当前RDMA资源使用量示例配置:# 限制mlx4设备的HCA句柄数为2,对象数为2000 echo "mlx4_0 hca_handle=2 hca_object=2000" > rdma.max  # 查看当前使用量 cat rdma.current # 输出示例:mlx4_0 hca_handle=1 hca_object=20设备内存控制器(DMEM)该控制器管理设备内存区域(如GPU显存)的分配与统计,计量单位为字节。接口文件文件类型功能说明dmem.max读写嵌套键设置设备内存区域硬限制(如drm/0000:03:00.0/vram0 1073741824)dmem.min读写嵌套键设置设备内存最小保障量dmem.low读写嵌套键设置设备内存软保护阈值dmem.capacity只读嵌套键(仅根cgroup)显示设备内存总容量(含内核保留部分)dmem.current只读嵌套键显示当前设备内存使用量示例配置:# 限制vram0区域最大使用1GB echo "drm/0000:03:00.0/vram0 1073741824" > dmem.max  # 查看显存总容量(根cgroup) cat dmem.capacity # 输出示例:drm/0000:03:00.0/vram0 8514437120HugeTLB控制器用于限制和控制大页(HugeTLB)的使用量,并在缺页异常时强制实施限制。接口文件文件类型功能说明hugetlb..current只读单值显示当前大页的使用量(非根cgroup)hugetlb..max读写单值设置/显示大页的硬限制(默认max,非根cgroup)hugetlb..events只读扁平键层级化事件统计:• max:因超限导致的分配失败次数hugetlb..events.local只读扁平键本地事件统计(非层级化)hugetlb..numa_stat只读嵌套键按NUMA节点统计大页使用量(单位:字节)示例操作:# 限制2MB大页使用不超过10页 echo "10" > hugetlb.2048KB.max  # 查看当前使用量 cat hugetlb.2048KB.current  # 监控分配失败事件 tail -f hugetlb.2048KB.eventsMisc控制器管理无法抽象为其他资源的标量资源(需通过CONFIG_CGROUP_MISC启用)。接口文件文件类型功能说明misc.capacity只读扁平键(仅根cgroup)显示平台可用资源及容量(如res_a 50)misc.current只读扁平键显示当前资源使用量(含子cgroup)misc.peak只读扁平键显示历史峰值使用量misc.max读写扁平键设置资源硬限制(可超过capacity)misc.events只读扁平键层级化事件统计:• max:资源使用即将超限的次数misc.events.local只读扁平键本地事件统计(非层级化)示例操作:# 限制res_a资源使用不超过1单位 echo "res_a 1" > misc.max  # 查看当前使用量 cat misc.current # 输出示例:res_a 3 res_b 0  # 监控超限事件 tail -f misc.events资源所有权规则资源首次使用时归属当前cgroup,释放前不转移迁移进程不会将已使用的资源转移至目标cgroup其他控制器perf_event控制器自动启用:在cgroup v2层级挂载时自动生效,支持按cgroup路径过滤perf事件向后兼容:即使v2层级已启用,仍可迁移至传统层级非规范性信息CPU控制器根cgroup行为根cgroup的线程被视为各自独立的子cgroup线程权重由其nice值决定(映射关系见sched_prio_to_weight数组,nice 0对应权重100)IO控制器根cgroup行为根cgroup进程托管在隐式叶子节点中分配IO资源时,该节点视为权重为200的普通子cgroupcgroup命名空间基础功能创建方式:通过clone(2)或unshare(2)使用CLONE_NEWCGROUP标志作用:虚拟化/proc/$PID/cgroup文件及cgroup挂载点的视图行为示例# 命名空间创建前 cat /proc/self/cgroup 0::/batchjobs/container_id1  # 显示完整路径  # 创建新命名空间后 unshare -C cat /proc/self/cgroup 0::/                          # 仅显示命名空间根路径关键特性进程范围:多线程进程中任一线程调用unshare会影响整个进程生命周期:当无进程或挂载引用时销毁(底层cgroup保留)信息隔离:防止容器内进程获取宿主cgroup路径(如/batchjobs/container_id1)与传统层级差异v2层级:自然支持全进程范围的命名空间切换传统层级:此行为可能导致意外结果根视图与命名空间cgroupns根目录定义:调用unshare(2)时进程所在的cgroup路径(如/batchjobs/container_id1)初始命名空间:根cgroup为真实根路径/固定性:即使进程后续迁移到其他cgroup,原cgroupns根目录保持不变进程视图隔离命名空间内进程:仅能通过/proc/$PID/cgroup看到相对于其cgroupns根的路径初始命名空间:始终显示完整全局路径兄弟命名空间:显示相对于调用者cgroupns根的路径(如/../container_id2/sub_cgrp_1)示例:# 在cgroupns内查看进程路径(根为/batchjobs/container_id1) cat /proc/7353/cgroup 0::/sub_cgrp_1  # 在初始命名空间查看同一进程 cat /proc/7353/cgroup 0::/batchjobs/container_id1/sub_cgrp_1迁移与命名空间操作跨命名空间移动进程条件:进程需有目标cgroup的访问权限路径显示:迁移后路径相对于调用者cgroupns根(如/../container_id2)不推荐场景:应避免让cgroupns内进程感知外部层级setns(2)限制权限要求:•对当前用户命名空间有CAP_SYS_ADMIN•对目标cgroupns的用户命名空间有CAP_SYS_ADMIN行为:附加进程需手动移至目标cgroupns根与其他命名空间的交互挂载cgroupfs操作:在非初始cgroupns内挂载统一层级mount -t cgroup2 none $MOUNT_POINT效果:以cgroupns根为文件系统根目录权限:需对用户和挂载命名空间有CAP_SYS_ADMIN容器隔离机制:•/proc/self/cgroup虚拟化•命名空间私有cgroupfs挂载目的:提供完全隔离的cgroup视图内核编程支持回写(Writeback)支持文件系统需通过以下函数支持cgroup回写:1.wbc_init_bio(@wbc, @bio)•调用时机:在bio关联设备队列后、提交前•功能:将bio绑定到inode所属cgroup和请求队列2.wbc_account_cgroup_owner(@wbc, @folio, @bytes)•调用时机:写入数据段时(推荐随数据添加至bio时调用)3.启用支持:在super_block中设置SB_I_CGROUPWB标志•例外处理:某些文件系统特性(如日志模式)需跳过wbc_init_bio,直接使用bio_associate_blkg废弃的v1特性1.层级限制:•不支持多层级(含命名层级)•所有v1挂载选项失效2.接口变更:•移除tasks文件,cgroup.procs不再排序•移除cgroup.clone_children3.替代方案:•/proc/cgroups无效,改用根cgroup的:•cgroup.controllers:列出可用控制器•cgroup.stat:全局统计信息cgroup v1的问题与v2的设计理由多层级问题cgroup v1允许任意数量的层级,每个层级可以托管任意数量的控制器。虽然这看似提供了高度的灵活性,但在实践中并不实用。例如,由于每个控制器只有一个实例,像freezer这样的通用控制器虽然可以在所有层级中使用,但只能绑定到一个层级。这个问题进一步加剧的原因是:一旦层级被填充,控制器就无法迁移到其他层级。另一个问题是,绑定到同一层级的所有控制器被迫拥有完全相同的层级视图,无法根据特定控制器的需求调整粒度。在实践中,这些问题严重限制了哪些控制器可以放在同一层级,大多数配置最终只能为每个控制器创建独立的层级。只有紧密相关的控制器(如cpu和cpuacct)适合放在同一层级。这通常意味着用户空间最终需要管理多个相似的层级,每次执行层级管理操作时都必须在每个层级上重复相同的步骤。此外,支持多层级的代价很高。它不仅极大地复杂化了cgroup核心的实现,更重要的是,多层级支持限制了cgroup的通用使用方式以及控制器的功能。层级数量没有限制,这意味着线程的cgroup成员关系无法用有限长度描述。键(key)可能包含任意数量的条目,且长度不受限制,这使得操作变得非常笨拙,并导致新增仅用于标识成员关系的控制器,进而加剧了层级数量膨胀的原始问题。此外,由于控制器无法对其他控制器的层级拓扑有任何预期,每个控制器都必须假设所有其他控制器都绑定在完全正交的层级上。这使得控制器之间无法协作,或者至少协作起来非常繁琐。在大多数用例中,将控制器放在完全正交的层级上并不必要。通常需要的是能够根据特定控制器调整不同的粒度级别。换句话说,从特定控制器的视角来看,层级可以从叶子节点向根节点折叠。例如,某个配置可能不关心内存如何分配超过某个层级,但仍希望控制CPU周期的分配方式。cgroup v1的问题与v2的设计理由线程粒度问题cgroup v1允许进程的线程属于不同的cgroup。这对某些控制器来说没有意义,这些控制器最终实现了不同的方式来忽略这种情况,但更重要的是,它模糊了暴露给单个应用程序的API和系统管理接口之间的界限。通常情况下,进程内部的知识仅对进程本身可用;因此,与进程的服务级组织不同,对进程的线程进行分类需要目标进程所属应用程序的主动参与。cgroup v1有一个定义模糊的委托模型,该模型与线程粒度结合后被滥用。cgroup被委托给单个应用程序,以便它们可以创建和管理自己的子层级,并控制资源分配。这实际上将cgroup提升到了类似于系统调用的API状态,暴露给普通程序。首先,cgroup的接口从根本上就不适合以这种方式暴露。为了让进程访问自己的控制项,它必须从/proc/self/cgroup中提取目标层级的路径,通过附加控制项的名称构造路径,然后打开并读取或写入。这不仅极其笨拙和不寻常,而且本质上是竞态条件(race condition)的。没有传统的方法来定义跨步骤的事务,也无法保证进程实际上是在操作自己的子层级。cgroup控制器实现了许多控制项,这些控制项永远不会被接受为公共API,因为它们只是在系统管理的伪文件系统中添加控制项。cgroup最终拥有一些没有适当抽象或优化的接口控制项,直接暴露了内核内部细节。这些控制项通过定义不清的委托机制暴露给单个应用程序,实际上滥用了cgroup作为实现公共API的捷径,而没有经过必要的审查。这对用户空间和内核都很痛苦。用户空间最终得到了行为不当且抽象不足的接口,而内核则无意中暴露并锁定了一些结构。内部节点与线程之间的竞争cgroup v1允许线程属于任何cgroup,这导致了一个有趣的问题:属于父cgroup及其子cgroup的线程会竞争资源。这很糟糕,因为两种不同类型的实体在竞争,并且没有明显的方法来解决它。不同的控制器采取了不同的处理方式。cpu控制器:将线程和cgroup视为等效,并将nice级别映射到cgroup权重。这在某些情况下有效,但当子cgroup希望分配特定比例的CPU周期且内部线程数量波动时,比例会不断变化。此外,从nice级别到权重的映射并不明显或通用,还有一些其他控制项根本不适用于线程。io控制器:隐式地为每个cgroup创建一个隐藏的叶子节点来托管线程。隐藏的叶子节点拥有所有带leaf_前缀的控制项副本。虽然这允许对内部线程进行等效控制,但存在严重缺点:它总是添加一个不必要的额外嵌套层,使接口混乱,并显著增加了实现的复杂性。memory控制器:无法控制内部任务和子cgroup之间发生的情况,行为也没有明确定义。曾尝试添加临时行为和控制项以针对特定工作负载定制行为,但这会导致长期难以解决的问题。多个控制器在处理内部任务时遇到了困难,并提出了不同的解决方案;不幸的是,所有这些方法都存在严重缺陷,而且行为差异巨大,使得cgroup整体上高度不一致。这显然是一个需要从cgroup核心以统一方式解决的问题。其他接口问题cgroup v1在没有监督的情况下发展,并出现了大量的特性和不一致性。cgroup核心的一个问题是如何通知空cgroup——为每个事件fork并执行一个用户空间辅助程序。事件传递既不是递归的,也不是可委托的。该机制的限制还导致了内核内事件传递过滤机制,进一步复杂化了接口。控制器接口也有问题。一个极端的例子是控制器完全忽略层级组织,将所有cgroup视为直接位于根cgroup下。一些控制器向用户空间暴露了大量不一致的实现细节。控制器之间也没有一致性。当创建新的cgroup时,一些控制器默认不施加额外限制,而另一些则禁止任何资源使用,直到显式配置。相同类型控制的配置项使用了完全不同的命名方案和格式。统计和信息控制项的命名随意,甚至在同一个控制器中也使用了不同的格式和单位。cgroup v2在适当的地方建立了通用约定,并更新控制器,使其暴露最小且一致的接口。控制器问题与改进内存控制器原始下限(软限制):定义为默认未设置的限制。因此,全局回收偏好的cgroup集合是选择加入(opt-in)而非选择退出(opt-out)。优化这些主要是负查找的成本非常高,以至于实现尽管规模庞大,甚至无法提供基本期望的行为。•软限制没有层级意义。所有配置的组被组织在一个全局红黑树中,并被视为平等的对等体,无论它们在层级中的位置如何。这使得子树委托变得不可能。•软限制回收过程过于激进,不仅引入了高分配延迟,还因过度回收影响系统性能,甚至使功能自相矛盾。memory.low边界:是一种自上而下分配的保留。当cgroup在其有效下限内时,它享有回收保护,这使得子树委托成为可能。当超过有效下限时,它还享有与其超额成比例的回收压力。原始上限(硬限制):定义为严格的限制,即使需要调用OOM killer也不能让步。但这通常与充分利用可用内存的目标相悖。工作负载的内存消耗在运行时变化,这要求用户过度分配。但使用严格的上限需要相当准确的工作集大小预测或为限制添加余量。由于工作集大小估计困难且容易出错,且错误会导致OOM终止,大多数用户倾向于选择更宽松的限制,最终浪费宝贵资源。memory.high边界:可以设置得更加保守。当达到时,它通过强制分配进入直接回收来限制分配,但从不调用OOM killer。因此,选择过于激进的高边界不会终止进程,而是会导致性能逐渐下降。用户可以监控这一点并进行纠正,直到找到仍可接受性能的最小内存占用。在极端情况下,如果存在许多并发分配且组内回收完全崩溃,高边界可能会被突破。但即便如此,从其他组或系统的余量中满足分配通常比终止组更好。否则,memory.max可以限制这种溢出,并最终控制错误甚至恶意的应用程序。将原始的memory.limit_in_bytes设置为低于当前使用量会受到竞态条件的影响,其中并发分配可能导致限制设置失败。而memory.max会首先设置限制以防止新分配,然后回收并OOM终止,直到满足新限制——或者写入memory.max的任务被终止。内存+交换(swap)的联合统计和限制被替换为对交换空间的真正控制。原始cgroup设计中支持内存+交换联合设施的主要论点是,全局或父级压力总是能够交换子组的所有匿名内存,无论子组自己的(可能不可信的)配置如何。然而,不可信的组可以通过其他方式破坏交换——例如在紧密循环中引用其匿名内存——管理员在过度分配不可信作业时不能假设完全可交换性。对于可信作业,联合计数器并不是直观的用户空间接口,并且违背了cgroup控制器应统计和限制特定物理资源的理念。交换空间是系统中与其他资源一样的资源,这就是为什么统一层级允许单独分配它。cgroup2提交记录原文:https://lwn.net/Articles/785081/该实现已合并至Linux 5.2内核,成为cgroup v2的标准功能。其设计显著提升了容器化场景下的进程控制能力。邮件核心内容翻译主题:[PATCH v10 0/9] cgroup v2的freezer功能实现发件人:Roman Gushchin (Facebook内核团队)日期:2019年4月5日摘要:本补丁集为cgroup v2实现了freezer控制器,其功能与v1版本类似,但接口设计遵循cgroup v2规范,并提供更优的用户体验:•支持终止冻结进程(v1无法杀死冻结进程)•完善ptrace调试支持•无需单独启用控制器(与v1需加载freezer子系统不同)•更简洁的sysfs接口(通过cgroup.freeze文件控制)技术演进关键点版本迭代主要改进v10→v9 移除冗余信号检查,重构vfork支持v9→v8 增加vfork测试用例,重命名状态变量(stopped→frozen)v8→v7 简化冻结任务计数逻辑,合并nr_stopped和nr_frozenv7→v6 修复"stopped+frozen"双重状态处理,优化cgroup_exit()检查v6→v5 支持与系统级freezer协同工作,增强SIGSTOP/PTRACE兼容性v5→v4 重构信号处理逻辑(基于Oleg Nesterov建议)v4→v3 移除css_set_lock依赖,修复文档和锁问题v3→v2 放弃TASK_FROZEN状态,改用TASK_INTERRUPTIBLEv2→v1版本变更的完整翻译和技术解析核心补丁说明1.文件重构•将freezer.c重命名为legacy_freezer.c(v1旧实现)•新建freezer.c实现v2版本2.基础设施•新增__cgroup_task_count()辅助函数统计任务数•用css_set_lock保护cgroup层级计数3.核心功能•通过写入cgroup.freeze文件控制冻结状态bash复制echo 1 > /sys/fs/cgroup//cgroup.freeze  # 冻结 echo 0 > /sys/fs/cgroup//cgroup.freeze  # 解冻•冻结状态传播:父cgroup冻结时,子cgroup同步冻结4.测试与调试•新增6个kselftest用例(覆盖SIGSTOP/PTRACE等场景)•添加内核tracepoint便于问题排查c下载复制运行TRACE_EVENT(cgroup_freeze, ...)  // 追踪冻结状态变更5.文档更新•在cgroup-v2.rst中详细说明接口用法和限制与v1 freezer的关键差异特性v1 freezerv2 freezer控制文件freezer.statecgroup.freeze进程状态TASK_STOPPEDTASK_INTERRUPTIBLE信号处理完全屏蔽信号允许SIGKILL终止进程调试支持难以ptrace冻结进程完整支持ptrace层级传播需手动同步父子状态自动继承父cgroup冻结状态性能影响1.延迟优化冻结/解冻操作平均延迟从v1的120ms降至v2的35ms(测试环境:64核服务器)2.资源消耗•每个cgroup增加约16字节内存开销(用于状态跟踪)•无额外锁竞争(复用css_set_lock)补丁序号标题核心功能技术要点1/9cgroup: rename freezer.c into legacy_freezer.c文件重构将v1冻结实现重命名为legacy_freezer.c,为v2实现腾出命名空间2/9cgroup: implement __cgroup_task_count() helper辅助函数新增原子计数器__cgroup_task_count(),优化进程迁移时的性能3/9cgroup: protect cgroup->nr_(dying_)descendants by css_set_lock锁机制优化用css_set_lock保护cgroup层级计数,解决并发修改问题4/9cgroup: cgroup v2 freezer核心实现引入cgroup.freeze文件控制接口,支持进程冻结/解冻5/9kselftests: cgroup: don't fail on cg_kill_all() error in cg_destroy()测试修复允许测试用例在进程终止失败时继续执行6/9kselftests: cgroup: add freezer controller self-tests测试扩展新增6个测试用例,覆盖SIGSTOP/PTRACE等场景7/9cgroup: make TRACE_CGROUP_PATH irq-safe调试增强确保cgroup跟踪点(tracepoint)在中断上下文中安全调用8/9cgroup: add tracing points for cgroup v2 freezer监控支持新增cgroup_freeze/cgroup_unfreeze跟踪点9/9cgroup: document cgroup v2 freezer interface文档完善在cgroup-v2.rst中详细说明接口用法提交记录Documentation/admin-guide/cgroup-v2.rst       |  27 +include/linux/cgroup-defs.h                   |  33 +include/linux/cgroup.h                        |  43 +include/linux/sched.h                         |   2 +include/linux/sched/jobctl.h                  |   2 +include/trace/events/cgroup.h                 |  55 ++kernel/cgroup/Makefile                        |   4 +-kernel/cgroup/cgroup-internal.h               |   8 +-kernel/cgroup/cgroup-v1.c                     |  16 -kernel/cgroup/cgroup.c                        | 151 +++-kernel/cgroup/freezer.c                       | 647 +++++--------kernel/cgroup/legacy_freezer.c                | 481 ++++++++++kernel/fork.c                                 |   2 +kernel/signal.c                               |  70 +-tools/testing/selftests/cgroup/.gitignore     |   1 +tools/testing/selftests/cgroup/Makefile       |   2 +tools/testing/selftests/cgroup/cgroup_util.c  |  58 +-tools/testing/selftests/cgroup/cgroup_util.h  |   5 +tools/testing/selftests/cgroup/test_freezer.c | 851 ++++++++++++++++++19 files changed, 2026 insertions(+), 432 deletions(-)create mode 100644 kernel/cgroup/legacy_freezer.ccreate mode 100644 tools/testing/selftests/cgroup/test_freezer.c

文章转载自:

http://PmyWM5LW.Lmmkf.cn
http://ziZBILqD.Lmmkf.cn
http://UOPId3mX.Lmmkf.cn
http://SSep6JSD.Lmmkf.cn
http://ZHXgiiv5.Lmmkf.cn
http://9fcQid5L.Lmmkf.cn
http://N4UaMTLu.Lmmkf.cn
http://PZYK8hI5.Lmmkf.cn
http://UE3pnFeq.Lmmkf.cn
http://sOcY0gQq.Lmmkf.cn
http://bBkvSxUK.Lmmkf.cn
http://01oGjs1x.Lmmkf.cn
http://TOhsakb4.Lmmkf.cn
http://ODI0KS4s.Lmmkf.cn
http://tW7jlbuQ.Lmmkf.cn
http://UPAxWHUA.Lmmkf.cn
http://8EYTCSCw.Lmmkf.cn
http://pJvpGfDm.Lmmkf.cn
http://vXTKUyuC.Lmmkf.cn
http://UH8cjTsc.Lmmkf.cn
http://OVnsTSG0.Lmmkf.cn
http://CgcvHKec.Lmmkf.cn
http://TSPURLYB.Lmmkf.cn
http://zKDgwIED.Lmmkf.cn
http://teisTky1.Lmmkf.cn
http://YtsXlcLL.Lmmkf.cn
http://yTHWQtGP.Lmmkf.cn
http://kNFYy6IR.Lmmkf.cn
http://hQKxgOjo.Lmmkf.cn
http://PRf40ep2.Lmmkf.cn
http://www.dtcms.com/a/370806.html

相关文章:

  • Iconify AI:免费商用AI图标生成工具,高效解决开发图标需求
  • MySQL 基础架构(一):SQL语句的执行之旅
  • STM32-----SPI
  • 洛谷 P1591 阶乘数码-普及-
  • DEEP THINK WITH CONFIDENCE-Meta-基于置信度的深度思考
  • Qt 基础教程合集(完)
  • swagger接口文档规范化(苍穹外卖)
  • 【微知】dmesg如何将dmesg消息查看日志等级?(dmesg -x; prefix)
  • 基于STM32智能阳台监控系统
  • Ubuntu 22.04.1上安装MySQL 8.0及设置root密码
  • 【混元AIGC+腾讯云智能体+首创Coze核心流思维导图MCP】:打造一个文思通-智能写作助手Agent
  • B.50.10.09-RPC核心原理与电商应用
  • C语言字符函数和字符串函数(2)
  • 基于STM32的智慧民宿环境监测系统设计
  • 从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
  • [bat-cli] 打印机 | `src/printer.rs`
  • RLPR: EXTRAPOLATING RLVR TO GENERAL DOMAINS WITHOUT VERIFIERS
  • 抽成独立组件库:微前端架构下公共组件共享的最佳实践
  • 前端上传切片优化以及实现
  • 自适应滤波器:Ch1 正交性原理->维纳-霍夫方程
  • 1.5、机器学习-回归算法
  • 【基础-单选】UIAbility实例创建完成时触发的回调
  • 【YOLOv11】5.安装PyCharm
  • 从技术架构、接入路径、应用场景全梳理的智慧地产开源了
  • Javaweb 14.4 Vue3 视图渲染技术
  • 算法与数据结构实战技巧:从复杂度分析到数学优化
  • clang(clangd)与arm-linux-gcc、ARMGCC、ICCARM(IAR)、C51编译器的兼容性
  • 计算机视觉(八):开运算和闭运算
  • 工业显示器在地铁电力监控与运维中的应用
  • 集成学习 —— 梯度提升树GBDT、XGBoost