Linux cgroup v2 io.max 如何限制设备带宽
Linux 上想给某个服务限制磁盘读写,关键不是给挂载目录“限速”,而是把进程放进 cgroup v2,再在该 cgroup 的 io.max 中按块设备的 MAJ:MIN 写入上限。比如目标设备是 8:16,可以设置每秒最多读取 2 MiB、每秒最多写入 8 MiB;未配置的方向仍是 max。
最小闭环是:确认 cgroup v2 和io控制器可用,创建专用 cgroup,向io.max写入设备号与rbps/wbps,然后用io.stat和进程归属文件复查。限额到达后 I/O 会被延迟,短时突发仍可能出现。
io.max的键是设备主次号,不是/data这类路径。rbps/wbps是字节/秒,riops/wiops是 I/O 次数/秒;写max可以移除某个限制。- 验证时要同时看进程是否在目标 cgroup,以及
io.stat是否出现对应设备行。
io.max 限制的对象是设备,而不是挂载目录
cgroup v2 的资源控制是层级模型:进程先归属于一个 cgroup,控制器再针对这个层级中的资源消耗施加边界。io.max 由 I/O 控制器提供,配置行使用块设备的主设备号和次设备号,例如 8:16。因此,同一个挂载目录如果跨越多个块设备,就不能只写一个目录名来表达限制。
这个接口支持四个独立维度:rbps 和 wbps 分别限制读、写字节速率;riops 和 wiops 分别限制读、写 I/O 次数。它们可以在同一行按任意顺序组合。内核文档还特别说明,BPS 和 IOPS 按读写方向分别计量,达到限制时请求会延迟,但临时突发并不会被完全禁止。

| 字段 | 含义 | 示例 |
|---|---|---|
MAJ:MIN | 块设备主次设备号 | 8:16 |
rbps / wbps | 读 / 写字节每秒 | 2097152 |
riops / wiops | 读 / 写 I/O 次数每秒 | 120 |
max | 移除该字段的具体限制 | wiops=max |
先确认 cgroup v2 和 io 控制器
先找出统一层级挂载点,不要假定所有发行版都使用相同目录。下面的检查只读取状态;真正写入控制器前,应确认你管理的是专用层级,并了解发行版的 systemd 是否会接管该目录。
# 查看 cgroup v2 的挂载点;输出为空时先处理层级配置
findmnt -t cgroup2 -o TARGET,FSTYPE,OPTIONS
# 以常见挂载点为例,确认 io 控制器是否对父级可用
CG=/sys/fs/cgroup
cat "$CG/cgroup.controllers"
cat "$CG/cgroup.subtree_control"
# 创建专用工作负载边界;生产环境请使用明确的服务生命周期管理
mkdir -p "$CG/limited-worker"
echo "+io" > "$CG/cgroup.subtree_control"
cat "$CG/limited-worker/cgroup.controllers"
控制器是向下分配的:父级没有在 cgroup.subtree_control 启用 io,子级就不能越过父级单独启用。不同发行版可能由 systemd 维护 cgroup 树;如果手工目录和服务管理器争用同一层级,重启或重新加载服务后配置可能被覆盖,最好选定一种管理方式并记录归属。
按主次设备号写入 rbps 与 wbps
用设备文件而不是挂载路径得到 MAJ:MIN。下面以示例设备 /dev/sdb 和工作负载目录为例:读限制设为 2 MiB/s,写限制设为 8 MiB/s。数值单位是字节,不是“MiB”字符串,因此 2 MiB 要写成 2097152。
# 从块设备读取十进制主次设备号,避免凭路径猜设备
DEVICE=/dev/sdb
DEVNO=$(lsblk -no MAJ:MIN "$DEVICE")
CG=/sys/fs/cgroup/limited-worker
# 同一设备行一次写入读写字节上限;值按字节/秒计算
echo "$DEVNO rbps=2097152 wbps=8388608" > "$CG/io.max"
# 立即回读,确认未限制方向仍显示为 max
grep -F "$DEVNO" "$CG/io.max"
如果还要限制请求次数,可以在同一行加入 riops= 或 wiops=。不要把同一个键在一行中重复写入;内核文档明确指出这种结果未定义。取消单项限制时保留设备号,只把对应值改为 max:
# 只移除写字节速率限制,读限制保持不变
echo "$DEVNO wbps=max" > "$CG/io.max"
cat "$CG/io.max"
限额是 cgroup 对该设备的绝对边界,不是整块盘的性能承诺。父级已有更紧限制时,子级不能把它“放宽”;多个子 cgroup 的限额之和也不应被理解为设备一定能同时提供的吞吐量。
用 io.stat 和进程归属复查是否生效
只看到 io.max 的配置并不能证明目标服务正在受控。先把服务 PID 放入 cgroup,再检查三处:进程的 /proc/PID/cgroup、目标目录的 cgroup.procs,以及该 cgroup 的 io.stat。
# 将已经启动的工作负载迁入目标 cgroup;写入 PID 会迁移该进程的线程组
PID=12345
echo "$PID" > "$CG/cgroup.procs"
# 两种方向交叉确认进程归属
grep '0::' "/proc/$PID/cgroup"
grep -Fx "$PID" "$CG/cgroup.procs"
# 按设备查看累计读写字节与 I/O 次数
grep -F "$DEVNO" "$CG/io.stat"
io.stat 里的常见字段包括 rbytes、wbytes、rios 和 wios,设备行同样以 MAJ:MIN 标识。它适合确认统计归属和变化趋势,不应拿一次读数当成稳定带宽基准;需要判断限流效果时,应在固定负载下按时间窗口观察业务吞吐、请求延迟和 I/O 压力。

上线前保留回退和审计边界
生产变更至少记录四项:目标服务和 PID 归属、设备的 MAJ:MIN、每个读写字段的旧值与新值、回退命令。若只想撤销限制,可以对相应字段写 max;若要整体删除临时 cgroup,应先停止或迁出其中进程,再按发行版管理方式清理目录。
还要注意缓存写回的边界。内核文档说明,cgroup writeback 需要底层文件系统支持,当前明确支持 ext2、ext4、btrfs、f2fs 和 xfs;其他文件系统的写回 I/O 可能归到 root cgroup。多个 cgroup 同时修改同一个 inode 时,归属也可能不符合直觉。因此,“配置回读正确”和“业务看到的吞吐下降”是两个都要观察的结果。
最终检查可以压缩成这张清单:父级有 io、目标目录有 io.max;设备号来自实际块设备;服务 PID 出现在目标 cgroup;io.stat 有对应设备行;systemd 或其他管理器不会在重启时覆盖配置;回退值和审计记录已保存。
相关问题
io.max 能直接限制某个挂载目录吗?
不能直接按目录匹配。它按块设备主次号生效;如果目录对应多个设备,需要分别确认并配置各设备。
rbps 和 riops 应该选哪个?
需要限制总读写量时选 BPS,需要限制请求频率时选 IOPS;两者可以组合,但都应结合实际负载观察,而不是只凭设备标称值设定。
为什么 io.stat 有数据但体感没有明显限速?
先确认 PID 在目标 cgroup,再看读写是否经过页缓存、写回文件系统是否支持,以及限制是否配置在真正发生 I/O 的设备上。
如何取消一个设备的写限制?
向该 cgroup 的 io.max 写入同一设备号和 wbps=max;回读文件确认该字段恢复为 max。
参考资料
- Linux Kernel:Control Group v2,查看控制器层级、io.max 字段和 io.stat 说明。
- freedesktop.org:systemd.resource-control,了解 systemd 服务与资源控制的管理边界。
Go JSON 数字进 interface 后为什么变成 float64
- 上一篇
- Go JSON 数字进 interface 后为什么变成 float64
- 下一篇
- ReadableStream 如何把 Fetch 响应分块显示到页面
-
- 文章 · linux | 19小时前 |
- Linux cgroup v2 memory.max 如何判断进程被限制
- 216浏览 收藏
-
- 文章 · linux | 19小时前 | Linux · 日志管理 · 运维排障 · 文件描述符 logrotate copytruncate 日志轮替 postrotate
- Linux logrotate rotate 后应用仍写旧文件怎么办
- 285浏览 收藏
-
- 文章 · linux | 21小时前 |
- Linux ulimit -n 调高后仍报 too many open files
- 275浏览 收藏
-
- 文章 · linux | 22小时前 | Linux · 文件系统 · 挂载 · 运维排障 · 工作目录 bind mount Linux mount --bind umount target is busy EBUSY
- Linux mount --bind 解除挂载时为什么要先离开工作目录
- 476浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 98次使用
-
- OpenCompass
- OpenCompass是上海AI实验室推出的开源大模型评测平台,提供CompassKit、CompassHub和CompassRank三大核心组件,支持LLM及多模态模型的一站式标准化评估与排行榜查询。
- 28次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 253次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 180次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 114次使用
-
- Go语言中的IO操作及Flag包的用法
- 2022-12-30 491浏览
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览
-
- Linux系统下Go语言开发环境搭建
- 2023-01-07 242浏览
-
- Go语言io pipe源码分析详情
- 2023-01-07 237浏览

