登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

cgroup v2 io.weight 调整块设备相对优先级

来源:17golang原创

时间:2026-10-10 18:33:38 310浏览 收藏

我第一次用 cgroup v2 调整磁盘任务优先级时,最容易误解的是把 io.weight 当成“给这个服务固定多少 MB/s”。它实际表达的是同级 cgroup 在有竞争时对 I/O 资源的相对份额:默认值是 100,可写范围是 1 到 10000。因此,两个都在忙的同级组可以用 100 和 500 表达大约五比一的权重关系,但这不是硬带宽保证。

官方地址:https://docs.kernel.org/admin-guide/cgroup-v2.html

把 io.weight 当成“争用时的相对优先级”,把 io.max 当成“绝对 BPS/IOPS 限制”,再确认当前块设备和 I/O 控制器支持权重,配置才不会看起来写成功、实际却没有可观察差异。

io.weight 到底调整了什么

cgroup v2 的 io 控制器管理 I/O 资源分配。io.weight 只存在于非根 cgroup,默认行通常是 default 100;除此之外,还可以按设备的 major:minor 写覆盖项。权重越高,表示该 cgroup 在和兄弟组同时竞争同一个 I/O 资源时可以获得更高的相对份额。

这里有两个很重要的前提。第一,权重是同级比较,不是跨整棵层级树直接比较;第二,只有真正活跃、正在争用资源的组才会参与分配。空闲组不会因为配置了很高的数字就预先占走带宽,所以单独压测一个组时,常常看不出 100 和 500 的差别。

cgroup v2 io.weight 在同级 cgroup 和同一块设备之间表达相对 I/O 份额的结构示意图
图1:io.weight 在同级 cgroup 之间表达块设备 I/O 相对份额的结构说明图,不是截图或运行证据。

内核文档还明确区分了控制器实现:绝对 BPS/IOPS 限制由 blk-throttle 提供;权重型比例控制由 iocost 成本模型,以及使用 BFQ 调度器时的 BFQ cgroup 支持提供。因此,写入文件成功不等于当前设备一定会按预期表现出比例差异。

先准备两个真正同级的 cgroup

我会先把实验缩小成两个同级目录,让它们运行相同类型的 I/O 工作负载。下面假定系统已经挂载 cgroup v2,并且当前 shell 具备创建层级、启用控制器和迁移进程所需的权限。示例只展示配置边界,不会替你选择生产服务。

# 查看当前是否是 cgroup v2,以及 io 控制器是否可用
stat -fc %T /sys/fs/cgroup
cat /sys/fs/cgroup/cgroup.controllers

# 建立两个同级工作组,父级需要先把 io 控制器下放给子树
mkdir -p /sys/fs/cgroup/io-background /sys/fs/cgroup/io-interactive
echo +io > /sys/fs/cgroup/cgroup.subtree_control

# 让两个工作组拥有明确的默认权重,便于后续观察相对关系
echo "default 100" > /sys/fs/cgroup/io-background/io.weight
echo "default 500" > /sys/fs/cgroup/io-interactive/io.weight

# 把目标进程的 PID 写入 cgroup.procs;生产环境应使用服务管理器统一迁移
echo "$PID_BACKGROUND" > /sys/fs/cgroup/io-background/cgroup.procs
echo "$PID_INTERACTIVE" > /sys/fs/cgroup/io-interactive/cgroup.procs

cgroup.subtree_control 的写入必须发生在父级,且子组中不能有不满足层级规则的进程;如果收到权限、忙或层级相关错误,先处理层级状态,不要把它误判为权重值不生效。实际服务通常由 systemd、容器运行时或编排器管理 cgroup,直接写系统树更适合隔离实验。

给某个块设备单独设置权重

默认权重适合“这个 cgroup 对所有相关设备都采用同一优先级”的场景。如果一个组在系统盘和数据盘上的策略不同,可以追加 major:minor 设备覆盖。先用 lsblk 找到设备号,再把覆盖写入对应 cgroup 的 io.weight。

# 读取块设备名称和 major:minor,避免凭设备名猜测覆盖目标
lsblk -o NAME,MAJ:MIN,MOUNTPOINTS

# 假定目标设备为 8:16,只提高交互组在该设备上的相对权重
echo "8:16 500" > /sys/fs/cgroup/io-interactive/io.weight

# 同一个文件会同时显示默认值和设备覆盖,读取结果确认写入格式
cat /sys/fs/cgroup/io-interactive/io.weight

设备覆盖的格式是 major:minor weight,默认值则可以写成单独的数字或 default weight。不要把 8:16 当作永远代表某块固定磁盘:设备重建、虚拟机映射和容器环境都可能改变 major:minor,生产配置应由启动时发现结果或明确的设备管理流程生成。

cgroup v2 io.weight 的 default 权重、major:minor 设备覆盖和 io.stat 观察关系示意图
图2:为 major:minor 块设备设置 io.weight 覆盖并观察 io.stat 的结构说明图,不是截图或运行证据。

为什么单看配置文件还不够

我在调这个参数时,第一次复查只看到了 io.weight 里的数字,后来才发现测试盘上根本没有两个组同时产生足够的 I/O。复查至少要把“配置被接受”和“争用时出现预期行为”分成两件事。

# 查看两个组的权重配置,确认默认项或设备覆盖确实存在
cat /sys/fs/cgroup/io-background/io.weight
cat /sys/fs/cgroup/io-interactive/io.weight

# 读取按 major:minor 统计的读写字节、IO 次数和丢弃数据
cat /sys/fs/cgroup/io-background/io.stat
cat /sys/fs/cgroup/io-interactive/io.stat

# 在两个组都持续读写同一设备时重复采样,才有机会观察比例差异
sleep 5
cat /sys/fs/cgroup/io-background/io.stat
cat /sys/fs/cgroup/io-interactive/io.stat

io.stat 提供按设备记录的读写字节数、读写 I/O 次数以及 discard 统计。它能告诉你两个组实际对哪些设备产生了多少 I/O,但不能单独证明某个测试已经达到稳定比例。要做有意义的比较,应固定文件集、并发度和测试时长,让两个组同时工作,并记录设备调度器、缓存影响和测试阶段。

如果写入 io.weight 没有报错,却始终观察不到差别,优先排查四件事:是否真的为 cgroup v2;两个工作负载是否位于同一父级;目标设备是否支持当前内核路径的权重控制;测试是否存在足够竞争。不要只把数字从 500 改到 5000,然后把无差异归因于“内核忽略了配置”。

权重、硬限制和清理动作怎么取舍

io.weight 适合“都要跑,但某类工作更重要”的场景,例如交互任务和后台归档共同使用一块盘。若需求是“这个服务最多只能读 20 MB/s 或每秒 5000 次 I/O”,应看 io.max,而不是把权重调得很低。权重不会把上限固定在某个吞吐量上,也不会在没有竞争时主动制造限速。

# 清除某个设备的专用覆盖,让它回到该 cgroup 的 default 权重
echo "8:16 default" > /sys/fs/cgroup/io-interactive/io.weight

# 重新读取文件,确认该设备覆盖不再出现在结果中
cat /sys/fs/cgroup/io-interactive/io.weight

# 实验结束后先停止或迁回工作进程,再移除空的 cgroup 目录
# 下面的路径只是示意,生产环境应由服务管理器执行回收
rmdir /sys/fs/cgroup/io-background
rmdir /sys/fs/cgroup/io-interactive

对我来说,最稳妥的配置顺序是:先用默认权重验证两个同级组能产生可重复的争用,再给单一设备加覆盖,最后才考虑和 io.max 组合。这样每次改动只改变一个变量,也更容易从 io.stat 和调度器状态中解释结果。

常见问题

io.weight 设成 1000,是不是固定获得 1000 份带宽?

不是。它是相对权重,只有与同级、同时活跃的 cgroup 发生竞争时才有比较意义;实际份额还受设备、调度器、缓存和工作负载影响。

为什么只给一个 cgroup 设置高权重看不出变化?

没有竞争时,资源本来就可能被它独占。请让至少两个同级组同时访问同一设备,并用 io.stat 采样,而不是只读取配置文件。

io.weight 和 io.max 应该选哪个?

需要相对优先级时选 io.weight;需要绝对 BPS 或 IOPS 上限时看 io.max。两者可以在明确目标后组合使用。

设备覆盖删除后为什么又出现了?

常见原因是启动脚本、systemd 或容器运行时再次写入配置,也可能是设备 major:minor 发生变化。应检查实际的 cgroup 管理者和启动流程。

cgroup v2 的 io.weight 最适合做“相对优先级”这件事:先保证两个工作组在正确层级中,再确认目标设备支持权重控制,最后在真实争用期间结合 io.stat 判断。只记住一个数字而忽略层级和设备条件,通常比不调参数更难排查。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>