直接读写 /sys/fs/cgroup 是唯一方式;v1 各子系统独立挂载(如 /sys/fs/cgroup/memory),v2 统一挂载(如 /sys/fs/cgroup),需通过 /proc/1/cgroup 或 /proc/cgroups 判断版本;c# 写 memory.max 或 cpu.max 必须用 file.openwrite 同步全量覆盖,严格格式(如 1g、50000 100000),root 权限,目录预创建,容器内需显式启用控制器且确认挂载为可写。

Linux cgroup v1/v2 文件系统路径怎么找
直接读写 /sys/fs/cgroup 下的文件是唯一可行方式,C# 本身不提供抽象 API。cgroup v1 和 v2 路径结构不同,混用会读不到值或写失败。
- v1:每个子系统独立挂载,如
/sys/fs/cgroup/memory、/sys/fs/cgroup/cpu - v2:统一挂载点,通常为
/sys/fs/cgroup,所有控制器(如memory.max、cpu.max)在同一目录下,但需确认是否启用unified模式(cat /proc/cgroups看hierarchy列是否为 0) - 运行时判断:检查
/proc/1/cgroup内容,v2 下该文件第二列是0::/,v1 是数字 ID + 路径
用 C# 写 memory.max 或 cpu.max 文件要注意什么
必须用 StreamWriter 全量覆盖写入,不能追加;值格式严格,空格、单位、换行都可能触发 Invalid argument 错误。
-
memory.max接受字节数(如1073741824)或带单位字符串(1G),但不支持1g(小写)、1 GB(带空格) -
cpu.max格式是max period,例如50000 100000表示 50ms 运行时间 / 100ms 周期;两个数字间只能有一个空格,结尾不能有换行或空格 - 写入前确保目标 cgroup 目录已存在(如
/sys/fs/cgroup/myapp),否则会报No such file or directory;可用mkdir -p预创建,C# 中调用Process.Start("mkdir", "-p /sys/fs/cgroup/myapp")(需 root)
为什么 FileStream.WriteAsync 总是卡住或报 Permission denied
cgroup 文件是内核虚拟文件,不支持常规文件操作语义。很多方法会静默失败或阻塞,必须用最简路径:同步写 + 明确编码 + 无缓冲。
- 别用
File.WriteAllText—— 它内部可能加 BOM 或换行,导致写入失败 - 必须用
File.OpenWrite+Stream.Write,并指定Encoding.UTF8(虽然内容是 ASCII,但 .NET 默认可能用其他编码) - 必须以 root 权限运行进程,普通用户即使
sudo启动程序也不行,除非用sudo dotnet run或部署为 systemd service 并配置PermissionsStartOnly=true - 写入后建议立即读回验证,比如写完
memory.max后读memory.current看是否生效,避免以为成功实则被内核忽略
限制容器内 .NET 进程自身资源的常见误区
在容器里跑 C# 程序再去写 cgroup 文件,路径和权限比宿主机更复杂;不是所有 cgroup 控制器都默认可用。
- Docker/Kubernetes 默认只挂载部分控制器(如 v2 下常禁用
memory),需显式启用:docker run --memory=1g --cpus=1.0 ...才会让对应文件可写 - 容器内看到的
/sys/fs/cgroup是只读 bind mount?检查挂载选项:findmnt -t cgroup2,若含ro就无法写入 - 写
tasks文件迁移进程时,.NET 的多线程 runtime 可能瞬间 spawn 新线程,导致部分线程没被移入;应先写cgroup.procs(只迁主线程 PID),再写tasks(迁所有线程),且需重试
Cgroup 文件系统没有“事务”或“回滚”,写错值(比如 memory.max 写成 0)会导致进程立刻被 OOM kill,而且不会报错——它只是静默生效。










