首页 / 资讯中心 / 文章详情

Linux memory cgroup 创建失败的根因诊断与实战修复

Linux memory cgroup 创建失败的根因诊断与实战修复 ★ FEATURED ARTICLE
1. 项目概述为什么“创建 memory cgroup 失败”是运维和开发绕不开的硬骨头“创建 memory cgroup 失败”这八个字听上去像一句系统报错日志里的冷冰冰提示但对任何在 Linux 容器、Kubernetes 或资源隔离场景下摸爬滚打过的工程师来说它背后往往是一连串真实而紧迫的问题服务启动卡在 init 阶段、Pod 一直 Pending、容器内存限制不生效、甚至整个节点资源调度突然失灵。这不是理论问题而是你凌晨三点收到告警时SSH 连上服务器第一眼就可能看到的拦路虎。核心关键词——memory cgroup、cgroup v1/v2、kernel 参数、OOM killer、systemd、cgroup.procs 写入失败——每一个都直指 Linux 资源管理的底层神经。它解决的不是“能不能用”而是“能不能稳、能不能准、能不能信”。适合谁Kubernetes 集群管理员、容器平台开发者、嵌入式系统资源优化人员、CI/CD 流水线稳定性保障工程师以及所有需要对进程内存行为做确定性约束的技术角色。它不挑人但极其挑细节一个内核配置开关没开、一行 systemd 服务配置写错、一次 mount 命令挂载顺序颠倒都足以让整个 memory cgroup 层级树在初始化阶段就崩塌。我经历过三次典型故障第一次是某边缘计算节点升级内核后cgroup v2 默认启用但容器运行时未适配所有 memory 子系统创建全返回 ENOENT第二次是某金融后台服务在 systemd 启动脚本里漏加 MemoryMax导致 cgroup 目录虽存在却无法写入限制值OOM 时直接杀掉主进程而非子进程第三次最隐蔽——某定制发行版裁剪了 CONFIG_MEMCG_SWAPy结果 swap accounting 关闭memory.max 文件根本不会生成debugfs 下连路径都找不到。这些都不是文档里会重点标红的坑而是实操中必须亲手踩过、记牢、形成肌肉记忆的经验。2. 核心机制拆解memory cgroup 不是“建个目录”那么简单2.1 从 cgroup v1 到 v2两套完全不同的资源治理哲学很多人以为“创建 memory cgroup”就是mkdir /sys/fs/cgroup/memory/myapp然后往里面写点数字完事。这种理解在 cgroup v1 下勉强能跑通但在 v2 下会直接撞墙。根本原因在于v1 是“按控制器分治”v2 是“统一层级统管”。v1 时代memory、cpu、pids 各自独立挂载你可以只挂 memory不挂 cpu互不影响。但 v2 强制要求所有控制器包括 memory必须挂载在同一棵 cgroup 树上且只能挂一次。这意味着如果你执行了mount -t cgroup2 none /sys/fs/cgroup那 memory 就不再是单独的子系统而是这棵统一树上的一个可配置属性。此时再试图mkdir /sys/fs/cgroup/memory/xxx系统会直接返回No such file or directory——因为/sys/fs/cgroup/memory/这个路径压根不存在v2 下只有/sys/fs/cgroup/xxx这种扁平结构。验证方法极简单stat /sys/fs/cgroup/ | grep Type如果输出cgroup2你就已经身处 v2 世界如果输出cgroup无数字才是 v1。这个判断必须放在所有操作之前否则后续所有命令都是南辕北辙。我见过太多人对着 v2 环境死磕 v1 的文档反复尝试echo 100M /sys/fs/cgroup/memory/myapp/memory.limit_in_bytes结果永远报错直到发现目录根本不存在。v2 下等效操作是mkdir /sys/fs/cgroup/myapp echo 100000000 /sys/fs/cgroup/myapp/memory.max。注意单位v1 用limit_in_bytesv2 用memory.max且 v2 的值支持max不限制、0禁止使用内存、具体字节数还新增了high、low、min等精细化阈值这是 v1 完全不具备的弹性控制能力。2.2 kernel 编译选项那些藏在 .config 里的“隐形开关”即使你确认了 cgroup 版本创建失败仍可能源于内核本身就没编译进 memory cgroup 支持。这不是运行时配置问题而是出厂设置缺失。关键编译选项有三个缺一不可CONFIG_CGROUPSycgroup 框架总开关必须为 y内置或 m模块。若为 n整个 cgroup 系统都不存在。CONFIG_MEMCGymemory cgroup 子系统核心开关。若为 n/sys/fs/cgroup/memory/v1或memory.maxv2文件根本不会生成。CONFIG_MEMCG_SWAPy控制是否启用 swap 内存统计。若为 nv1 下memory.memsw.limit_in_bytes不可用v2 下memory.swap.max文件不会出现。很多生产环境为简化管理会关闭 swap但若业务逻辑依赖 swap accounting如某些 JVM GC 策略关闭它就会导致预期外的 OOM 行为。如何快速验证zcat /proc/config.gz | grep -E CONFIG_CGROUPS|CONFIG_MEMCG|CONFIG_MEMCG_SWAP需内核启用 IKCONFIG_PROC。若无此文件则查/boot/config-$(uname -r)。若发现CONFIG_MEMCGn恭喜你遇到的是最底层的硬伤——必须重新编译内核或更换已启用该选项的内核版本。这不是改个配置就能解决的而是架构级前提。我曾协助某物联网设备厂商排查其定制内核因追求精简将CONFIG_MEMCG设为n导致所有基于 cgroup 的内存隔离方案全部失效最终不得不回退到用户态内存监控信号干预的粗糙方案稳定性大打折扣。2.3 systemd 的深度耦合你以为在管 cgroup其实是在和 systemd 打交道在现代 Linux 发行版尤其是 RHEL/CentOS 8、Ubuntu 20.04、Debian 11中systemd已成为 cgroup 的事实管理者。它不再只是个 init 系统更是 cgroup 的“中央调度局”。当你执行systemctl start myapp.servicesystemd 会自动为该服务创建对应的 cgroup 路径如/sys/fs/cgroup/myapp.service并应用你在 service 文件中定义的资源限制。此时如果你手动mkdir /sys/fs/cgroup/myappsystemd 会认为这是非法冲突可能拒绝启动服务或在清理时误删你的手动目录。更关键的是systemd 对 cgroup 的管理有严格生命周期服务停止时它会自动递归删除整个 cgroup 目录。这意味着手动创建的 cgroup 在 systemd 环境下是“临时户口”而通过 systemd unit 定义的才是“正式编制”。验证方式systemctl show myapp.service | grep -i cgroup查看ControlGroup字段是否指向有效路径。若为空或报错说明 systemd 未成功为其创建 cgroup。常见陷阱是服务文件中写了MemoryMax512M但忘记设置Delegateyes。Delegate选项决定该服务是否有权在其子 cgroup 中进一步创建和管理子控制器。若为 no默认则服务进程无法向myapp.service/child/这类子路径写入限制导致嵌套容器或子进程内存控制失效。这是 Kubernetes kubelet 启动时必须显式设置Delegateyes的根本原因。2.4 权限与命名空间root 不等于无所不能即使 root 用户在容器或非初始 PID 命名空间中创建 memory cgroup 也会失败。这是因为 cgroup 的创建受CAP_SYS_ADMIN能力约束而该能力在非初始命名空间中默认被丢弃。docker run --cap-addSYS_ADMIN可以临时赋予但这违背最小权限原则且在 Kubernetes Pod Security Policy或新版 Pod Security Admission中会被拦截。更安全的做法是在宿主机上预先创建好所需 cgroup 目录并通过--cgroup-parent参数指定容器使用该父目录让容器进程仅继承已有 cgroup无需创建新层级。另一个常被忽视的权限点是cgroup.procs文件的写入。创建 cgroup 目录只是第一步真正生效需将进程 PID 写入cgroup.procs。但该文件有严格规则只能由进程自身或其父进程写入且目标进程必须与当前进程处于同一 cgroup 层级或子层级。这意味着你不能在一个 shell 中echo $$ /sys/fs/cgroup/myapp/cgroup.procs把自己移进去因为 shell 进程通常在/sys/fs/cgroup/unified/下而myapp是同级目录违反层级约束。正确做法是先mkdir /sys/fs/cgroup/myapp再sh -c echo $$ /sys/fs/cgroup/myapp/cgroup.procs; exec $ -- your_command利用子 shell 的父子关系完成迁移。这个细节决定了“创建”和“生效”之间隔着一道看不见的墙。3. 实操排障全流程从报错信息反推根因的七步法3.1 第一步精准捕获错误码——别只看“failed”要看 errno所有“创建失败”的表象背后都是具体的 errno错误号。这是诊断的起点绝不能跳过。常见错误码及其含义如下表所示错误码数值字符串表示典型触发场景关键检查点ENOENT2No such file or directorycgroup v2 环境下尝试访问/sys/fs/cgroup/memory/内核未启用CONFIG_MEMCG挂载点未创建stat /sys/fs/cgroup/确认 v1/v2grep CONFIG_MEMCG /boot/config-*EPERM1Operation not permitted非 root 用户操作进程在非初始 PID 命名空间cgroup.procs写入违反父子层级规则id确认 uidlsns -t pid查看命名空间检查cgroup.procs写入逻辑EACCES13Permission denied目录权限不足如chmod 700 /sys/fs/cgroupSELinux/AppArmor 策略阻止ls -ld /sys/fs/cgroup/ausearch -m avc -ts recentSELinuxENOSPC28No space left on devicecgroup v1 下memory.use_hierarchy1时子 cgroup 数量超过memory.kmem.limit_in_bytes限制v2 下memory.max设为 0cat /sys/fs/cgroup/memory/memory.use_hierarchy检查memory.kmem.limit_in_bytes值EBUSY16Device or resource busycgroup 目录已被其他进程占用尝试重复挂载同一类型 cgrouplsof D /sys/fs/cgroup/findmnt提示获取精确 errno 的最佳方式是使用strace。例如strace -e tracemkdir,mount,write -f systemctl start myapp.service 21 | grep -A5 -B5 failed。它能清晰显示哪个系统调用失败及返回的具体数值比单纯看 journal 日志可靠十倍。3.2 第二步验证 cgroup 挂载状态——mount 命令是你的第一面镜子mount | grep cgroup输出是诊断的黄金快照。一个健康的 cgroup v2 环境应类似cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,seclabel)而 cgroup v1 环境则会有多行如cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent/usr/lib/systemd/systemd-cgroups-agent,namesystemd) cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)关键观察点有三挂载类型cgroup2vscgroup直接定版本挂载选项v2 必须有cgroup2类型v1 下每行末尾括号内的memory、cpu等即为启用的控制器若memory缺失说明该控制器未挂载挂载点路径v1 下/sys/fs/cgroup/memory必须存在且可读v2 下/sys/fs/cgroup/必须存在且类型为cgroup2。若发现memory控制器在 v1 下未挂载手动挂载命令为mount -t cgroup -o memory none /sys/fs/cgroup/memory。但注意此操作在 systemd 管理的系统中可能被覆盖永久生效需修改/etc/fstab或 systemd mount unit。3.3 第三步检查内核参数与启动配置——/proc/cmdline 是启动时的“遗嘱”内核启动参数能覆盖编译选项的默认行为。cat /proc/cmdline输出中以下参数至关重要cgroup_enablememory强制启用 memory cgroup即使CONFIG_MEMCG为m模块此参数也能确保模块被加载。若缺失且CONFIG_MEMCGm则 memory cgroup 不可用。systemd.unified_cgroup_hierarchy1强制启用 cgroup v2。若为0或缺失则回退到 v1。systemd.legacy_systemd_cgroup_controller1在 v2 下启用 v1 兼容模式不推荐仅用于过渡。修改方法编辑/etc/default/grub在GRUB_CMDLINE_LINUX行添加所需参数如GRUB_CMDLINE_LINUXcgroup_enablememory systemd.unified_cgroup_hierarchy1然后update-grub reboot。这是解决“内核编译了但没启用”的终极手段。我曾处理一个案例客户内核CONFIG_MEMCGy但启动参数遗漏cgroup_enablememory导致ls /sys/fs/cgroup/下始终没有memory目录折腾两天才发现启动参数这道门没开。3.4 第四步systemd 服务单元深度审计——Unit 文件里的“暗礁”当故障发生在systemctl start时必须逐行审计 service 文件。一个健壮的、支持 memory cgroup 的 unit 示例及关键注释如下[Unit] DescriptionMy Memory-Constrained App Afternetwork.target [Service] Typesimple ExecStart/usr/local/bin/myapp # 必须开启委托否则子进程无法创建自己的 cgroup Delegateyes # 内存硬限制v2 下生效 MemoryMax512M # 内存软限制v2 下生效允许短暂超限 MemoryHigh400M # 防止 OOM killer 杀掉本进程优先杀子进程 OOMScoreAdjust-500 # 确保 cgroup 目录在服务启动前创建 RuntimeDirectorymyapp # 若需 swap 限制且内核启用了 CONFIG_MEMCG_SWAP MemorySwapMax1G [Install] WantedBymulti-user.target审计清单Delegateyes是否存在缺失则嵌套控制失效MemoryMax值是否为合法格式支持M,G,K后缀或纯数字MemoryMax512无单位会被解释为 512 字节瞬间 OOMOOMScoreAdjust是否设为负值正值会提高被 OOM killer 选中的概率RuntimeDirectory是否声明它确保/run/myapp/目录存在避免因路径不存在导致启动失败。3.5 第五步实时验证 cgroup 状态——不要相信“应该”要看见“正在”创建操作完成后必须立即验证状态而非假设成功。核心验证命令cat /sys/fs/cgroup/myapp/memory.maxv2或cat /sys/fs/cgroup/memory/myapp/memory.limit_in_bytesv1确认限制值已写入且未被重置为max或-1cat /sys/fs/cgroup/myapp/cgroup.procs确认目标进程 PID 已列出cat /sys/fs/cgroup/myapp/memory.currentv2或memory.usage_in_bytesv1确认计数器开始增长证明统计已激活ps -o pid,cgroup $(pgrep myapp)确认进程确实位于预期 cgroup 路径下。一个经典误判是mkdir成功、echo写入memory.max成功但cat memory.current始终为 0。这通常意味着进程并未真正运行在该 cgroup 下而是仍在父 cgroup如/sys/fs/cgroup/中。此时需检查cgroup.procs是否为空或ps命令输出是否匹配。3.6 第六步日志交叉分析——journalctl 与 dmesg 的双盲验证journalctl -u myapp.service -n 50 --no-pager是服务级日志但关键的 cgroup 底层错误往往出现在内核日志中。dmesg -T | grep -i cgroup\|memory\|oom能捕获 kernel panic 级别的 cgroup 初始化失败例如[Mon Jan 1 10:00:00 2024] cgroup: cgroup2: unknown option memory [Mon Jan 1 10:00:00 2024] Memory cgroup stats for /myapp: cache:0KB rss:0KB rss_huge:0KB mapped_file:0KB dirty:0KB writeback:0KB inactive_anon:0KB active_anon:0KB inactive_file:0KB active_file:0KB unevictable:0KB第一行明确指出挂载选项错误第二行则证明 cgroup 已创建且统计正常。两者结合能快速定位是“创建失败”还是“创建成功但统计异常”。我习惯同时打开两个终端窗口一个journalctl -f -u myapp一个dmesg -w | grep -i cgroup启动服务时同步观察错误源头几乎秒现。3.7 第七步最小化复现——剥离所有干扰回归本质当以上步骤均未定位根因必须构建最小化环境。步骤如下启动一个干净的bash非 systemd 管理的 shellunshare --user --pid --fork --mount-proc /bin/bash手动挂载 cgroup v2mkdir -p /tmp/cgroup mount -t cgroup2 none /tmp/cgroup创建子目录mkdir /tmp/cgroup/mytest尝试写入限制echo 100000000 /tmp/cgroup/mytest/memory.max启动测试进程并迁移sleep 1000 echo $! /tmp/cgroup/mytest/cgroup.procs观察cat /tmp/cgroup/mytest/memory.current是否增长。若此最小环境成功则问题必在 systemd、SELinux、或上游容器运行时如 containerd、cri-o的配置中若失败则问题锁定在内核或硬件层面如某些 ARM 平台对 cgroup v2 支持不完整。这个方法曾帮我快速排除一个棘手问题某 ARM64 服务器上memory.max写入后立即被内核重置为max最终发现是内核补丁缺失升级到 5.10.100 后解决。4. 解决方案库针对不同场景的“抄作业”式配置4.1 场景一Kubernetes Node 上 kubelet 启动失败报错 “failed to create memory cgroup”这是生产环境最高频的故障。kubelet 作为 Kubernetes 的节点代理重度依赖 cgroup 进行 Pod 资源隔离。失败原因通常是 cgroup v2 与旧版 kubelet 不兼容或 systemd 配置冲突。解决方案分三步第一步确认 kubelet 版本与 cgroup 兼容性kubelet 1.21仅支持 cgroup v1kubelet 1.21-1.24支持 v1/v2但需显式配置kubelet 1.25默认要求 cgroup v2。验证命令kubelet --version。若版本低于 1.21 但系统为 v2必须切换到 v1。第二步强制 kubelet 使用 v1适用于旧版 kubelet编辑/var/lib/kubelet/config.yaml添加cgroupDriver: cgroupfs并确保/etc/systemd/system/kubelet.service.d/10-kubeadm.conf中包含EnvironmentKUBELET_EXTRA_ARGS--cgroup-drivercgroupfs然后重启systemctl daemon-reload systemctl restart kubelet。第三步为 v2 环境配置 kubelet推荐面向未来确保内核启动参数含systemd.unified_cgroup_hierarchy1/var/lib/kubelet/config.yaml中设置cgroupDriver: systemd关键在/etc/systemd/system/kubelet.service.d/10-kubeadm.conf中必须添加[Service] Delegateyes这是 kubelet 能在 v2 下创建子 cgroup 的生命线。缺少它kubelet 会报failed to create cgroup path /kubepods.slice。注意Delegateyes必须写在[Service]段内且不能被其他文件覆盖。我曾因/etc/systemd/system/kubelet.service.d/20-custom.conf中错误地写了Delegateno导致所有配置失效排查耗时半天。4.2 场景二Docker 容器内存限制不生效docker run -m 512m后top显示内存远超Docker 本身不直接管理 cgroup它依赖底层的 containerd 和 runc。问题根源常在 containerd 配置。解决方案检查 containerd 配置cat /etc/containerd/config.toml确认以下配置存在且正确[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] runtime_type io.containerd.runc.v2 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true # 此项为关键必须为 true 才能使用 systemd cgroup 驱动若SystemdCgroup false默认值则 runc 使用cgroupfs驱动与 systemd 管理的 cgroup 冲突导致限制无效。验证与修复修改配置后systemctl restart containerd重启 Dockersystemctl restart docker启动测试容器docker run -it --rm -m 512m ubuntu:20.04 sh -c dd if/dev/zero of/dev/null bs1M count1000在宿主机上cat /sys/fs/cgroup/docker/*/memory.maxv2或memory.limit_in_bytesv1确认值为536870912。实操心得SystemdCgroup true是 Docker 在 systemd 环境下的“最佳实践开关”。它让容器 cgroup 完全融入 systemd 的统一管理避免双头管理带来的不一致。但需注意此设置要求 containerd 1.4 和较新内核。4.3 场景三自定义 systemd service 中MemoryMax设置后进程仍被 OOM killer 杀死这通常不是 cgroup 创建失败而是 OOM killer 的决策逻辑被误解。MemoryMax是硬限制一旦突破内核会立即触发 OOM killer。但 killer 选择杀谁取决于oom_score_adj值。解决方案精细调整 OOM 评分在 service 文件[Service]段中添加# 降低本服务被 kill 的优先级范围 -1000 到 1000-1000 为永不 kill OOMScoreAdjust-500 # 同时确保子进程继承更低的评分可选 OOMScoreAdjProcessyes重启服务后cat /proc/$(pgrep myapp)/oom_score_adj应返回-500。添加内存压力缓冲仅靠MemoryMax太刚性。v2 提供更柔性的MemoryHigh# 当内存使用接近 400M 时内核开始积极回收页面避免冲到 512M 硬限 MemoryHigh400M # 设置最低保障确保关键缓存不被回收 MemoryMin100MMemoryHigh触发的是内存回收reclaim而非 OOM killer用户体验更平滑。验证 OOM 行为启动服务后手动制造内存压力stress-ng --vm 1 --vm-bytes 600m --timeout 60s观察dmesg是否出现Out of memory: Killed process及被杀进程名。若myapp未被杀而是stress-ng被杀说明配置生效。4.4 场景四嵌入式设备ARM上 cgroup v2 初始化失败mount返回Invalid argument部分 ARM SoC如早期 Rockchip、Allwinner的内核对 cgroup v2 支持不完整尤其在memory控制器的 swap accounting 或 kmem accounting 上。解决方案是降级到 v1 并精简功能禁用高风险子功能编辑/etc/default/grub在GRUB_CMDLINE_LINUX中添加cgroup_enablememory swapaccount0 cgroup.memorynokmemswapaccount0禁用 swap accounting避免CONFIG_MEMCG_SWAP相关错误cgroup.memorynokmem禁用内核内存kmem accounting解决kmem.limit_in_bytes不可用问题。强制使用 v1添加systemd.unified_cgroup_hierarchy0并确保/etc/fstab中有cgroup /sys/fs/cgroup/memory cgroup memory,defaults 0 0然后update-grub reboot。经验之谈嵌入式场景下“够用就好”是铁律。v2 的先进特性在资源受限设备上可能是负担。我为某工业网关定制的方案就是彻底关闭 kmem 和 swap accounting仅保留基础 memory limit稳定运行三年无 cgroup 相关故障。5. 高阶避坑指南那些文档里不会写的实战血泪5.1 “/sys/fs/cgroup/” 目录被清空别急着 mkdir先查 systemd 的“自动清理”在 systemd 管理的系统中/sys/fs/cgroup/下的空目录并非“损坏”而是 systemd 的主动清理策略。systemd 默认启用DefaultControllers选项若未显式声明它会定期扫描并删除空的 cgroup 目录。因此你手动mkdir /sys/fs/cgroup/myapp后几秒钟就消失不是 bug是 feature。解决方案有两个永久方案在/etc/systemd/system.conf中设置DefaultControllersmemory cpu pids列出你需要的控制器并systemctl daemon-reload临时方案创建一个“占位”进程如sleep infinity /sys/fs/cgroup/myapp/cgroup.procs只要该进程存活目录就不会被删。我的教训曾为一个监控脚本创建临时 cgroup每次运行都手动 mkdir结果脚本在夜间被 systemd 清理导致监控中断。后来改为systemd-run --scope --scope-prefixmyapp-monitor --propertyMemoryMax100M bash -c your_monitor_script让 systemd 全权托管生命周期一劳永逸。5.2memory.max设为0的真实含义不是“不限”而是“禁止”这是最反直觉的坑。在 cgroup v2 中echo 0 memory.max并非设置无限内存而是完全禁止该 cgroup 使用任何内存。任何尝试分配内存的操作如malloc都会立即失败返回ENOMEM。这常被误用作“禁止进程运行”的手段但实际效果是进程在main()开始前就因libc初始化失败而退出。正确做法是若想“不限制”写max字符串echo max memory.max若想“严格限制为 0”仅用于调试或极端安全场景需确保进程本身不申请内存。验证cat memory.max输出0表示禁止max表示不限。5.3 容器内free -h显示内存与 cgroup 限制不符那是free的“善意谎言”free命令读取的是/proc/meminfo而该文件反映的是整个系统的物理内存状态不受 cgroup 限制影响。容器内看到的total是宿主机内存而非容器限额。要查看容器实际可用内存必须读取 cgroup 接口v2cat /sys/fs/cgroup/memory.max限额cat /sys/fs/cgroup/memory.current已用v1cat /sys/fs/cgroup/memory/memory.limit_in_bytesmemory.usage_in_bytes。free的误导性曾导致我们团队误判一个容器的内存泄漏——free显示用了 2G而memory.current只有 300M最终发现是free把宿主机的 buffer/cache 算进去了。记住cgroup 的真相永远在/sys/fs/cgroup/下不在/proc/meminfo里。5.4cgroup.procs与cgroup.tasks的生死之别写错一个字母控制权全失cgroup.procs和cgroup.tasks都在 cgroup 目录下但语义天壤之别cgroup.procs写入线程组 leader 的 PID即getpid()返回的值。写入后该线程组的所有线程包括后续 fork 的都进入此 cgroup。cgroup.tasks写入任意线程的 TID线程 ID。写入后仅该线程进入其子线程不继承。绝大多数场景应使用cgroup.procs。若误写cgroup.tasks则 fork 出的新线程会回到父 cgroup导致内存控制“漏气”。验证方法ps -o pid,tid,cgroup $(pgrep myapp)观察所有线程的cgroup列是否一致。最后分享一个小技巧在调试时用echo $$ /sys/fs/cgroup/myapp/cgroup.procs将当前 shell 移入 cgroup然后exec your_program这样整个程序及其所有子进程都受控比在程序内调用setgid更可靠。这是我写自动化部署脚本时的标准操作百试百灵。
阅读完成 · 觉得有帮助?
咨询建站