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

Linux cgroup v2 memory.swap.max 怎样限制交换空间

来源:17golang原创

时间:2026-10-23 08:59:34 336浏览 收藏

如果只想限制一个服务最多使用多少交换空间,Linux cgroup v2 应该写目标 cgroup 的 memory.swap.max,而不是把它和 memory.max 当成同一个开关。memory.max 限制的是 cgroup 的总内存使用,memory.swap.max 限制的是交换空间使用;两者可以同时设置,也可以只设置其中一个。

最小做法是:确认目标 cgroup 已启用 memory 控制器,把进程放入该 cgroup,再向 memory.swap.max 写入字节数,例如 512M。随后读取 memory.swap.current 和 memory.swap.events,用当前占用和失败计数判断配置是否真的落到了目标 cgroup。

先分清三个内存文件

memory.max 是内存使用硬上限,达到后内核会尝试回收,无法降低使用量时可能触发 cgroup 内的 OOM 处理。memory.swap.max 是交换空间硬上限;达到这个上限后,属于该 cgroup 的匿名内存不会继续被换出。memory.swap.current 只反映当前交换使用量,不是上限配置。

接口作用适合回答的问题
memory.max限制总内存使用这个 cgroup 最多能占多少内存?
memory.swap.max限制交换空间使用这个 cgroup 最多能使用多少 swap?
memory.swap.current读取当前交换占用现在用了多少 swap?
memory.swap.peak读取历史峰值从创建或重置以来最高用过多少?
memory.swap.events读取交换边界事件是否碰到过 high、max 或 fail?
cgroup v2 中 memory.max、memory.swap.max 与交换占用接口的关系说明图
图1:cgroup v2 内存上限、交换上限与读取接口的关系说明图;这是静态说明图,不是运行截图。

确认目标 cgroup 的控制器入口

下面的示例使用一个非根 cgroup。不同发行版的挂载路径可能不同,因此先从当前 cgroup 目录和控制器列表开始确认。不要直接把命令中的路径当成所有机器都固定不变的路径。

# 查看当前进程所属的 cgroup 层级和控制器列表
CGROUP_ROOT=/sys/fs/cgroup
TARGET="$CGROUP_ROOT/app-demo"

# 创建一个用于示例的非根 cgroup;目录已存在时不报错
sudo mkdir -p "$TARGET"

# 确认父层级允许向子层级分发 memory 控制器
cat "$CGROUP_ROOT/cgroup.controllers"
cat "$CGROUP_ROOT/cgroup.subtree_control"

# 让子层级获得 memory 控制器;重复写入时忽略已启用的情况
echo '+memory' | sudo tee "$CGROUP_ROOT/cgroup.subtree_control" >/dev/null

# 确认目标接口已经出现;缺少它通常说明层级或控制器尚未准备好
sudo ls "$TARGET"/memory.swap.max "$TARGET"/memory.swap.current

如果目标目录里没有这些文件,先检查父 cgroup 是否允许向下分发 memory 控制器,以及当前系统是否确实挂载了 cgroup v2。不要把“目录创建成功”误认为“memory 控制器已经生效”。

写入 memory.swap.max 设置交换上限

接口接收的是字节数,也支持内核文档定义的大小格式。写入 max 表示不设置交换硬上限;写入 512M 则把该 cgroup 的交换使用上限设为 512 MiB。写入前要先确认目标 cgroup 当前交换占用,避免把限制调得低于预期并立即触发后续分配失败。

# 把服务的交换空间上限设置为 512 MiB
TARGET=/sys/fs/cgroup/app-demo
echo '512M' | sudo tee "$TARGET/memory.swap.max" >/dev/null

# 读取回写值,确认内核接受了这个设置
sudo cat "$TARGET/memory.swap.max"

# 需要取消交换硬上限时使用 max,而不是删除控制文件
echo 'max' | sudo tee "$TARGET/memory.swap.max" >/dev/null

这项设置只限制目标 cgroup 的交换使用,不会把系统全局 swap 关掉,也不会自动改变 memory.max。如果服务同时受到总内存上限约束,实际行为要结合两个文件一起判断。

用 current、peak 和 events 判断结果

配置完成后,先看当前值,再看峰值和事件计数。memory.swap.peak 记录的是创建 cgroup 或上次重置以来的最大交换使用量;memory.swap.events 则用平面键值报告交换边界相关事件,适合放进巡检脚本。

# 一次读取上限、当前占用、历史峰值和事件计数
TARGET=/sys/fs/cgroup/app-demo
for file in memory.swap.max memory.swap.current memory.swap.peak memory.swap.events; do
    # 文件不存在时保留文件名,便于区分控制器未启用和数值为零
    printf '\n[%s]\n' "$file"
    sudo cat "$TARGET/$file"
done

# 把事件键值变成便于日志采集的单行输出
sudo awk '{printf "%s=%s ", $1, $2} END {print ""}' "$TARGET/memory.swap.events"

看到 memory.swap.current 为 0 并不表示上限没有生效,只说明当前没有被换出的匿名页。更有价值的是把 current、peak 和 events 放在同一次采集中比较,避免只盯一个瞬时值。

memory.swap.current、memory.swap.peak 与 memory.swap.events 的静态观测关系图
图2:交换占用、峰值和边界事件的观测关系说明图;这是静态说明图,不是运行截图。

降低限制后为什么旧占用不会立刻归零

如果把 memory.swap.max 调低到当前交换占用以下,已有交换条目可能会逐步回收,短时间内读取到的 memory.swap.current 仍可能高于新限制。这是渐进处理的边界,不应该据此判断写入失败。反过来,把上限调高也不会主动把已经换出的页全部换回内存。

# 先记录调整前的占用,避免把观察误差当成配置错误
TARGET=/sys/fs/cgroup/app-demo
BEFORE=$(sudo cat "$TARGET/memory.swap.current")
printf 'before=%s bytes\n' "$BEFORE"

# 将上限调低后再次读取;旧条目可能需要时间逐步回收
echo '256M' | sudo tee "$TARGET/memory.swap.max" >/dev/null
sudo cat "$TARGET/memory.swap.max"
sudo cat "$TARGET/memory.swap.current"

# 采集事件,确认是否出现 max 或 fail,而不是只看一次 current
sudo cat "$TARGET/memory.swap.events"

排查时还要注意两点:第一,子 cgroup 的交换使用会计入父层级的层次统计;第二,memory.swap.max 的作用范围是 cgroup,而不是某个单独的线程。若只把一个服务进程放入 cgroup,检查时要确认它的子进程没有留在别的层级。

常见问题

只设置 memory.swap.max 就能限制总内存吗?

不能。它只处理交换空间上限;总内存仍要看 memory.max、当前内存和回收压力。

把 memory.swap.max 写成 0 是不是关闭整个系统 swap?

不是。它只禁止该 cgroup 的匿名内存继续换出,不影响其他 cgroup 或系统范围的 swap 配置。

为什么 memory.swap.current 超过了刚写入的限制?

降低限制后,已有交换条目可能渐进回收,因此短时间内可能高于新值。应结合事件文件和连续采样判断。

memory.swap.peak 可以手动清零吗?

可以按内核接口语义向该文件写入非空内容来重置后续读取的峰值;重置前应先把旧峰值写入监控或日志。

本文示例围绕一个目标 cgroup 展开。真正接入服务管理时,先确认进程归属,再把 memory.swap.max、memory.swap.current 与 memory.swap.events 纳入同一份资源巡检记录,才能区分“没有发生交换”和“交换已经被上限阻止”。

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