想象你是一位智慧小区的物业总管。老式小区里,水、电、煤气各有一本台账,每栋楼要在不同部门各开一个户头;想查整栋楼总用电量,得东拼西凑几张表格。更头疼的是,你发给住户的钥匙五花八门,有的人不知道该找谁报修,整个小区一团乱麻。Linux 内核管理进程资源时也曾面临同样的混乱:cgroups v1 为每种资源建立一棵独立层级树,CPU、内存、IO 各自为政,协调困难、线程模型粗糙,也无法安全地把管理权交给子进程。cgroups v2 的设计,就像把小区升级成一张总平面图、一套物业系统、一份委派协议——统一层级树让资源管理一目了然,subtree_control 把子楼栋的钥匙放心交给楼长,systemd 则像物业 ERP,让整套治理流程自动化运转。这篇文章就用“智慧小区”的比喻,带你彻底看懂 cgroups v2 在容器资源隔离中的核心作用。

岗位关联

理解 cgroups v2 是云原生与 Kubernetes 资源治理的基座。掌握它能让你精准定位容器资源瓶颈、设计安全委派策略、熟练使用 systemd 集成,成为团队里真正“管得住资源”的 SRE 与平台工程师。

目录
  1. 可视化演示
  2. 基础演进:老物业的“一本台账”困局
  3. 统一层级:一张总平面图管到底
  4. 控制器委派:把楼栋钥匙交给楼长
  5. 核心控制器:水表电表如何计量
  6. systemd 集成:物业公司与 ERP
  7. 容器实践:智慧小区落地验收
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

基础演进:老物业的“一本台账”困局

老物业最头疼的是什么?账目分散。CPU、内存、IO 各有各的独立台账,d 每栋楼的住户要想被统一管理,得在各个部门分别登记。cgroups v1 就是这样:每个控制器(cpu、memory、blkio…)单独挂载一棵层级树,互不关联。

技术层面,v1 把 /sys/fs/cgroup/cpu、/sys/fs/cgroup/memory 等作为独立挂载点,每一棵树的目录结构可以完全不同。这带来两个致命伤:一是系统无法表达“同时按内存和 CPU 分组的进程集合”,因为组关系因资源而异;二是线程模型粗糙——向 tasks 文件写入线程组 ID 时会整体迁移所有线程,无法对单个线程做精细隔离。

cgroups v2 的设计目标因此非常明确:统一层级、安全委派、支持线程粒度。它把散落的“多本存折”合并成一张总账。

就像老小区每栋楼在自来水公司、电力公司、燃气公司各有一个户号,查一次总用量要跑三个窗口。v1 的架构在容器化早期够用,但随着多资源协同需求上升,它注定要被更统一的设计取代。

Tv1={Tr:r∈{cpu, memory, io, … }}\mathcal{T}_{\mathrm{v1}} = \{ T_r : r \in \{ \text{cpu},\, \text{memory},\, \text{io},\, \dots \} \}
这个公式表示 v1 时代的层级树不是一个,而是一个集合——每种资源控制器都有一棵独立的树,彼此没有横向协调。
  1. v1 层级树全集$\mathcal{T}_{\mathrm{v1}}$ 是一个集合,装下了 v1 全部独立层级树。
  2. 单棵资源树$T_r$ 表示资源类型 $r$ 对应的那棵树,例如 $T_{\text{cpu}}$ 就是 CPU 控制器的树。
  3. 资源类型集合$\{\text{cpu}, \text{memory}, \text{io}, \dots\}$ 罗列了可被独立挂载的控制器种类,树的数量随控制器数量增长。

统一层级:一张总平面图管到底

新物业上任第一件事,就是把全小区画成一张总平面图。每栋楼、每个住户都在同一张图上登记,不再分部门建台账。cgroups v2 的核心正是这种“单树”设计:所有控制器共享一棵以 /sys/fs/cgroup 为根的层级树。

这棵树的规则非常清晰:根 cgroup 是所有进程的祖先;每个内部节点(目录)代表一个“组织单元”,叶子节点才允许直接挂进程。也就是说,一个目录如果有了子目录,就不能同时把 PID 写进 cgroup.procs,这被称为内部节点约束(no internal process constraint)。这样的约束保证了资源统计不会歧义——一个进程只属于一条从根到叶子的唯一路径。

写入规则也更精细了:cgroup.procs 接受进程 ID(整个线程组),cgroup.threads 接受线程 ID,并且支持线程模式(threaded mode),让某些子树可以按线程维度管理。你可以在总平面图上细分到“某个房间里的某张床”。

这就像总平面图上,一栋楼的“整层”可以再分成多家住户,但整层本身不能同时住人又分户。统一层级消除了 v1 多树带来的协调难题,让资源控制、统计、迁移都变得可预期。

children(n)≠∅  ⇒  procs(n)=∅\text{children}(n) \neq \varnothing \;\Rightarrow\; \text{procs}(n) = \varnothing
只要节点 $n$ 有子节点,它就不能再直接挂进程。这是统一层级树最重要的“红线”,也是 v2 语义简洁的根基。
  1. 有子节点$\text{children}(n) \neq \varnothing$ 表示节点 $n$ 下面还有子 cgroup,它是一个“部门”而非“工位”。
  2. 推出无进程$\Rightarrow$ 是逻辑蕴含符号,读作“则”;$\text{procs}(n) = \varnothing$ 表示直接挂在 $n$ 上的进程集合为空。
  3. 约束含义一个目录只能二选一——要么当组织部门管理子目录,要么当工位直接挂任务,不能两头都占。

控制器委派:把楼栋钥匙交给楼长

物业不可能替每个楼栋操心每盏灯、每台空调。合理的方式是:把整栋楼的水电表控制权打包,交给楼长,楼长在自己权限范围内安排住户配额。cgroups v2 的控制器委派(delegation)就是干这个的。

委派的开关在父目录的 subtree_control 文件里。父 cgroup 往这个文件写入 +cpu +memory,就表示允许子 cgroup 启用 CPU 和内存控制器。一旦写入,子 cgroup 的持有者(可以是普通非特权用户)就能在自己的子树里创建更深的 cgroup、调整控制文件数值,无需 root 权限。更关键的是,委派是“按控制器逐个授予”的:父级不给的控制器,子级就算写了 +io 也会被拒绝。

这套机制在容器运行时中极为重要:runc 启动容器时,会先创建一个空目录作为容器 cgroup,在父级写入 subtree_control 授予控制器,然后把容器的 init 进程放进这个子树。init 进程于是“接管”了这棵子树的管理权,容器内可以自由创建子 cgroup,但无法逃逸到父级去动别人的资源。

生活里,物业把整栋楼的灯闸交给楼长,但楼长无权打开小区总电房。委派在容器世界中的意义就是“最小权限”:让容器拥有内部自治能力,同时牢牢守住宿主资源的边界。

Dchild={c∈R:c∈subtree_control(parent)}D_{\text{child}} = \{ c \in \mathcal{R} : c \in \text{subtree\_control}(\text{parent}) \}
子 cgroup 能用的控制器集合,恰好等于父级在 subtree_control 里明确授予的那些。这是一个“白名单”式的授权模型。
  1. 委派集合$D_{\text{child}}$ 表示子 cgroup 被允许启用的控制器集合。
  2. 控制器全集$\mathcal{R}$ 是所有控制器(cpu、memory、io、pids…)的集合,代表“理论上可选的全部权限”。
  3. 白名单过滤$c \in \text{subtree\_control}(\text{parent})$ 是一个成员判断,只有被父级写入授予名单的控制器 $c$ 才会进入 $D_{\text{child}}$。

核心控制器:水表电表如何计量

有了总平面图和委派协议,接下来是计量工具。楼栋里的水表、电表、燃气表各有各的读数方式,cgroups v2 的核心控制器也各司其职。

CPU 调度靠两个文件配合:cpu.weight 决定竞争时的权重比例,类似“分时电表”——多个 cgroup 抢 CPU 时,谁的权重大谁拿到更多时间片;cpu.max 则规定硬性上限,格式是 quota/period,比如 100000/100000 表示最多用满 1 个核,50000/100000 表示最多用 0.5 个核。

内存管理通过 memory.max 和 memory.high 双阈值实现。memory.max 是硬墙,超过会触发 OOM 杀进程;memory.high 是软限,超过会开始回收页缓存,但不会立即杀人。这种“先警告后动手”的设计,让系统在压力下有缓冲空间。

IO 与网络方面,io.weight 控制块设备读写带宽的竞争比例(同样基于权重);值得强调的是,v2 并不包含网络控制器,容器网络带宽一般通过 TC 或 eBPF 旁路实现,这也提醒我们:cgroup 并不是万能的“资源总闸”。

这些机制合起来,就像小区里既有点亮楼道灯的“分时电表”,也有防止爆管的“水压阀”——权重保证公平,配额保证上限,回收机制提供缓冲。

Si=wi∑j∈SwjS_i = \frac{w_i}{\sum_{j \in S} w_j}
当内存触达 memory.high 时,内核优先回收可回收部分。这个公式说明哪些内存可以被“请走”而不用杀进程。
  1. 可回收内存$R_{\text{reclaimable}}$ 是内核在压力下优先回收的内存总量。
  2. 页缓存$R_{\text{page cache}}$ 是文件读写的缓存内容,随时可以丢弃重建。
  3. 内核可回收对象$R_{\text{kernel objects}}$ 包括 dentry、inode 等内核结构,回收它们能腾出空间且不影响业务逻辑。

systemd 集成:物业公司与 ERP

有了计量工具,还需要一套物业管理系统把规则落地。systemd 在现代 Linux 发行版中扮演的正是这个角色:它把每个服务、用户会话都纳入统一的 cgroup 管理,形成“一个单元一棵子树”的格局。

systemd 的单元(unit)类型与 cgroup 路径有固定映射关系:.service 是一个服务,.scope 是一组临时进程,.slice 是资源分组容器,层层嵌套组成类似 .slice 路径的目录结构。配置层面,管理员可以在 unit 文件里直接写 CPUWeight=200、MemoryMax=2G、TasksMax=512 等指令,systemd 会自动把它们翻译成 cgroup 控制文件写入。例如设置 CPUWeight=200 后,systemd 会找到对应 cgroup 并写入 cpu.weight——你不需要碰任何内核接口。

更精彩的是委派实现:unit 里加一行 Delegate=yes,systemd 就会把这棵子树交给该服务自身管理,不会在服务结束后主动清理子树内容,也不会干预子树内部的 cgroup 操作。这正是容器运行时需要的——systemd 负责创建 Pod 的顶层 slice,runc 在 slice 下再创建容器 cgroup,两边各司其职。

systemd 对 cgroup v2 的深度集成,让“物业公司”有了真正的 ERP——管理员不再手动操作内核文件,而是通过熟悉的 unit 文件声明式管理资源。这也是为什么 Kubernetes 默认推荐 systemd cgroup driver:它保证宿主机服务、用户会话与容器各自拥有清晰的资源边界。

wslice=∑u∈slicewuw_{\text{slice}} = \sum_{u \in \text{slice}} w_u
一个 slice(资源分组容器)的权重是其内部所有服务的权重之和。给整个 slice 设权重,相当于给整栋楼统一调“总闸”。
  1. 总权重$w_{\text{slice}}$ 是 slice 的 cpu.weight,代表整栋楼在小区总资源里的竞争地位。
  2. 求和遍历$u \in \text{slice}$ 遍历该 slice 下所有服务/scope 单元。
  3. 聚合语义$w_u$ 是每个单元的权重,求和后构成 slice 的权重,子单元之间再按各自权重分配 slice 内部资源。

容器实践:智慧小区落地验收

图纸再漂亮,也要看实际交付。这一节我们看 cgroups v2 如何与容器运行时和运维体系配合,完成资源隔离的最后一公里。

首先是运行时配合。现代 Docker(20.10+)和 Kubernetes(1.22+)默认支持 cgroup v2。在 kubelet 配置 systemd cgroup driver 后,每个 Pod 会落到 kubepods slice 下,例如 /sys/fs/cgroup/kubepods.slice/kubepods-besteffort.slice/pod-xxx/,容器运行时再用 runc 在 Pod 目录下创建容器 cgroup 并写入资源限制。这套树状结构与 QoS 类别一一对应:Guaranteed 的 Pod 权重高、限制严格,Burstable 的 Pod 有弹性余量,BestEffort 则完全看运气。

运维配置上,systemd-run 是随手可用的利器。比如要对一个临时任务限流,可以执行 systemd-run --scope -p CPUWeight=10 -p MemoryMax=2G ./big_job,系统会为它创建一个 scope 并施加限制,任务结束自动清理。

排查故障时,重点观测三个文件:cpu.stat 里的 throttled_time 看出是否被限流,memory.events 里的 oom 计数判断是否被杀过,io.stat 追踪块设备读写。如果容器突然变慢且产生了大量 throttled_time,多半是 cpu.max 设得太紧;如果 memory.events 中 low 字段频繁增加,则是内存回收在持续挤压性能。

至此,“智慧小区”全部落地:统一层级让总平面图一目了然,控制器委派把钥匙交给楼长,核心控制器精确计量,systemd 提供自动化 ERP,容器实践则验证了整套体系在真实环境中的可靠性。

Utotal=∑p∈PodsquotapperiodpU_{\text{total}} = \sum_{p \in \text{Pods}} \frac{\text{quota}_p}{\text{period}_p}
把所有容器的 cpu.max 上限加起来,得到整机 CPU 预留总量。做容量规划时,这个值必须小于节点总核数,否则会出现“超卖”导致的调度抖动。
  1. 总预留$U_{\text{total}}$ 是全部容器硬性 CPU 上限的叠加,代表极限情况下可能占用的核数。
  2. 遍历 Pod$p \in \text{Pods}$ 遍历节点上所有 Pod 的容器。
  3. 上限求和$\text{quota}_p / \text{period}_p$ 是每个 Pod 的核数上限,加总后与节点核数比较,判断是否超卖。

AI 应用场景

场景一:多租户推理平台的 CPU 隔离

阶段:模型推理服务的资源编排与保障

解决的问题:多个租户共享 GPU 服务器时,某个突发推理任务会把 CPU 吃满,导致其他租户延迟飙升。传统做法靠人肉 kill 进程,既不优雅也不精准。利用 cgroups v2,平台为每个租户创建独立 slice,按租户等级配置 CPUWeight 与 cpu.max,硬性隔离突发流量。

具体案例:某 LLM 推理平台用 systemd-run --scope -p CPUWeight=100 -p MemoryMax=8G 启动低优先级批处理任务,同时为在线推理服务设置 CPUWeight=800、cpu.max=1600000/100000(16 核)。压测显示,批处理任务即使跑满,在线服务 P99 延迟波动从 45% 降到 5% 以内。

场景二:模型训练任务的“水电表”监控

阶段:训练任务性能调优与故障排查

解决的问题:分布式训练中,某个 worker 内存超限被杀,但训练框架常常感知不到,导致断点重来。通过读取 memory.events 中的 oom 计数和 memory.current 的变化曲线,可以在 OOM 前预警,并配合 memory.high 设置软限,让回收机制先于杀进程生效。

具体案例:某推荐模型训练任务频繁在凌晨挂掉,排查发现容器 memory.high 未设置,缓存堆积直接把 memory.max 撞穿。设置 memory.high = max - 1G 后,内核提前回收页缓存,训练任务连续一周零中断。

场景三:AI 服务的 QoS 分级治理

阶段:大规模服务稳定性保障

解决的问题:在线 AI 服务、离线特征任务、定期评测任务混部在同一 Kubernetes 集群,资源竞争激烈。通过 cgroup v2 的权重模型,把三类任务分别映射到高、中、低权重 slice,让竞争时的资源分配符合业务优先级。

具体案例:某搜索团队在集群中为在线服务设置 CPUWeight=512,离线任务 CPUWeight=128,评测任务 CPUWeight=32。CPU 饱和时,三者时间片比例约为 8:2:0.5,在线服务 RT 保持稳定。

Python 演示

python37 行
"""模拟 cgroup v2 CPU 时间片分配:权重比例 + 配额上限"""from dataclasses import dataclassfrom typing import List  @dataclassclass CGroup:    name: str    weight: int    quota: int = 100000  # cpu.max quota,单位 us    period: int = 100000  # cpu.max period,单位 us  def weighted_share(groups: List[CGroup]) -> dict:    """按 cpu.weight 计算竞争状态下的时间片占比"""    total = sum(g.weight for g in groups)    return {g.name: g.weight / total for g in groups}  def hard_limit(g: CGroup) -> float:    """cpu.max = quota/period 决定的 CPU 核数上限"""    return g.quota / g.period  if __name__ == "__main__":    online = CGroup("online", weight=800, quota=1600000, period=100000)    batch = CGroup("batch", weight=150, quota=600000, period=100000)    test = CGroup("test", weight=50, quota=200000, period=100000)    groups = [online, batch, test]     print("=== CPU 竞争时间片占比(权重) ===")    for name, ratio in weighted_share(groups).items():        print(f"  {name:<8} {ratio:.1%}")     print("\n=== CPU 硬性上限(cpu.max) ===")    for g in groups:        print(f"  {g.name:<8} {hard_limit(g):.2f} 核")
目的:用纯 Python 快速推演 cgroup v2 的 CPU 分配语义——先按权重算竞争占比,再按 quota/period 算硬性上限,帮助你在不碰真实内核的情况下理解配额组合效果。
  1. class CGroup定义了一个数据中心的数据类,weight 对应 cpu.weight,quota/period 对应 cpu.max。
  2. weighted_share(groups)复现了正文公式 $S_i = w_i / \sum w_j$,计算每个 cgroup 的竞争份额。
  3. total = sum(g.weight for g in groups)求所有权重总和作为分母,权重是相对值这一点在这里体现得最直观。
  4. hard_limit(g)直接返回 quota/period 的结果,对应“最多占多少核”。
  5. CGroup("online", weight=800, ...)模拟在线服务高权重,让它拿到绝大多数的竞争份额。
  6. print(f" {name:<8} {ratio:.1%}")用格式化输出展示分配结果,<8 左对齐让表格更整齐。

常见误区

误区我听说 cgroup 能管理资源,那给容器设个 cgroup 不就能限制网速了吗?为什么我的 `net_cls` 在 v2 下不好使了?

正解cgroups v2 的标准控制器列表里没有网络控制器。v1 时代的 net_cls、net_prio 并没有在 v2 中延续。要在容器里做带宽限制,通常借助 TC(traffic control)的 HTB 队列或 eBPF 方案(如 Cilium 的 bandwidth manager)。cgroup 只负责“分组”,网络限速需要另一套“路闸”。

为何常见:人们容易把“资源”一概而论,忽略 CPU/内存是主动调度型、而网络是异步 I/O 型,管理机制差异很大。

误区我给容器设了 `memory.max=2G`,如果进程超过 2G,内核应该立刻把它杀掉吧?

正解不一定。内核优先回收可回收内存(页缓存、dentry 等),只有不可回收部分(匿名页)持续增长并撞到 memory.max 才会触发 OOM 杀进程。正确做法是同时设置 memory.high 作为软限,并监控 memory.events 里的 oom、high 计数来判断实际发生了什么。

为何常见:把“硬上限”理解为“立即超杀”,忽略了内核内存回收机制的存在。

误区既然子 cgroup 能自己调权重、配限额,那这个子 cgroup 的用户岂不是有 root 权限了?

正解委派只是把特定控制器在特定子树内的管理权交出去。被委派方只能操作自己拥有的子树里的控制文件,无法修改父级或其他子树的配置,更不能跨出子树访问宿主资源。它类似于物业把楼栋配电箱的钥匙交给楼长,但楼长打不开小区总电房。

为何常见:容易把“控制权”和“系统权限”混为一谈,忽略了 cgroup 本身只是内核 VFS 上的一组文件接口。

误区容器里不该跑 systemd,Kubernetes 也不该用 systemd 管理 cgroup,会互相干扰。

正解恰恰相反,现代 Kubernetes 默认推荐 systemd cgroup driver。systemd 的 Delegate=yes 机制会把子树管理权交给容器运行时,双方各管一层:systemd 管理宿主机服务拓扑,runc/containerd 管理容器内部资源。只要 driver 一致,协作非常顺畅。

为何常见:早期 Docker 与 systemd 的 cgroup 驱动不一致确实出过问题,但那是“driver 不匹配”的问题,而不是 systemd 本身的问题。

误区统一层级后,父目录不启用某个控制器,子目录也不能自己启用,所以资源分配完全被父级卡死。

正解父级确实可以通过 subtree_control 控制子级可用的控制器,但 v2 的设计允许“按需启用”——某个控制器只在需要的子树里启用,其他子树不受影响。同时,线程模式(threaded)为特殊场景提供了灵活性。统一的是“树的拓扑”,不是“每个节点都要启用所有控制器”。

为何常见:把“统一层级”误读为“统一配置”,忽略了 subtree_control 提供的细粒度启停能力。

面试题

Q1. cgroups v2 在 Linux 中的默认挂载点是哪个?

单选题 · easy

  • A./sys/fs/cgroup← 正确答案
  • B./cgroup
  • C./sys/fs/cgroup/v2
  • D./proc/cgroup

答案:A。cgroups v2 统一挂载在 /sys/fs/cgroup,这是 v2“单挂载点”设计的重要体现。选项 B、C、D 都是常见混淆项:/cgroup 是旧式挂载位置,/sys/fs/cgroup/v2 是多余路径,/proc/cgroup 是进程的 cgroup 关联信息而非挂载点。

Q2. cgroups v2 相比 v1 最核心的架构变化是什么?

单选题 · easy

  • A.增加了更多资源控制器
  • B.所有控制器共享同一棵层级树← 正确答案
  • C.移除了 CPU 控制器
  • D.只允许 root 用户操作

答案:B。v2 的核心是统一层级(unified hierarchy),所有控制器在 /sys/fs/cgroup 下共存一棵树,解决 v1“一控制器一树”的协调难题。A 项不是架构变化,C、D 与事实相反。

Q3. 在 cgroups v2 中,哪个文件决定子 cgroup 可以启用哪些控制器?

单选题 · mid

  • A.cgroup.procs
  • B.cgroup.controllers
  • C.subtree_control← 正确答案
  • D.cpu.max

答案:C。subtree_control 是委派的关键文件,父级写入 +cpu +memory 才允许子级启用这些控制器。cgroup.procs 用于挂进程,cgroup.controllers 只读展示可用控制器,cpu.max 是 CPU 限制参数。

Q4. 关于 cgroups v2 的“内部节点约束”,下列说法正确的是?

单选题 · mid

  • A.内部节点可以挂进程
  • B.有子节点的目录不能再直接挂进程← 正确答案
  • C.根节点不能创建子节点
  • D.只有叶子节点可以创建子节点

答案:B。内部节点约束规定:一个 cgroup 目录只要有子 cgroup,就不能再直接挂进程,避免资源统计歧义。A 与约束相反,C 错误(根可以创建子节点),D 混淆了方向(内部节点才能创建子节点)。

Q5. 某 cgroup 配置 cpu.max 为 50000/100000,它最多能使用多少 CPU 核?

单选题 · hard

  • A.2 核
  • B.1 核
  • C.0.5 核← 正确答案
  • D.5 核

答案:C。cpu.max 的语义是 quota/period 微秒比值,50000/100000 = 0.5,即最多半个核。它表示在一个结算周期内最多消耗 50ms CPU 时间。A、B、D 都是换算错误。

Q6. 当 cgroup 内存超过 memory.high 但未超过 memory.max 时,内核会怎么做?

单选题 · mid

  • A.立即杀死进程
  • B.触发 OOM Killer
  • C.开始回收内存并可能限制增长← 正确答案
  • D.不采取任何动作

答案:C。memory.high 是软限,超过后内核会尽力回收页缓存等可回收内存,并对内存增长施加压力,但不保证立即杀进程。A、B 是 memory.max 的场景,D 与 memory.high 的设计初衷矛盾。

Q7. 以下哪些是 systemd 中与 cgroups v2 相关的正确配置或机制?

多选题 · mid

  • A.CPUWeight= 设置 CPU 权重← 正确答案
  • B.MemoryMax= 设置内存硬上限← 正确答案
  • C.Delegate=yes 将子树委派给服务← 正确答案
  • D.CGroup= 强制删除宿主机所有 cgroup

答案:ABC。CPUWeight= 对应 cpu.weight,MemoryMax= 对应 memory.max,Delegate=yes 让 systemd 交出子树管理权,三者都是标准机制。D 项纯属虚构,systemd 没有任何配置会删除宿主机所有 cgroup。

Q8. 关于 cgroups v2 的控制器委派,下列说法正确的有?

多选题 · hard

  • A.非特权用户可以管理被委派的子树← 正确答案
  • B.委派后父 cgroup 不再统计子树的资源用量
  • C.父级可以针对单个控制器逐一下放权限← 正确答案
  • D.被委派方可以修改父 cgroup 的配置

答案:AC。委派的核心是“子树内自治”——非特权用户在拥有的子树里创建 cgroup 并配参数(A 对),父级通过 subtree_control 逐控制器下放权限(C 对)。子树统计始终会向上聚合到父级(B 错),被委派方无法越权修改父级配置(D 错)。

Q9. 排查容器内存问题时,应该优先关注哪些文件或指标?

多选题 · mid

  • A.memory.max 的配置值← 正确答案
  • B.memory.events 中的 oom / high 计数← 正确答案
  • C.cpu.stat 中的 throttled_time
  • D.io.weight 的权重值

答案:AB。memory.max 决定硬墙位置,memory.events 记录是否发生过 OOM 或频繁触达 high,是内存故障的核心证据。cpu.stat 是 CPU 限流指标,io.weight 是块设备权重,与内存排查无关。

Q10. 关于 cgroups v2 的线程模式(threaded mode),下列说法正确的是?

单选题 · extreme

  • A.允许内部节点直接挂线程← 正确答案
  • B.会消除所有内部节点约束
  • C.只能用于根 cgroup
  • D.线程模式下不能使用 CPU 控制器

答案:A。线程模式是一种例外机制:当子树启用 threaded 后,树内的内部节点可以挂线程,适用于按线程维度的精细化隔离场景。它并非取消所有约束(B 错),也不是根 cgroup 专有(C 错),CPU 控制器在线程模式下依旧可用(D 错)。