☰
虚拟交换空间解析:从zram到zswap的Linux现代内存管理
2026/10/15 7:01:59 网站建设 项目流程

如果现在还有人问你交换空间是不是就一块闲置磁盘分区,你最好重新想一想。我在整理 Linux 内存管理笔记时,几乎每翻几页都会撞见同一个主题:现代化交换机制下的“虚拟交换空间”。它不是某个发行版新挖出来的时髦名词,而是一整套让内核从固定后端走向抽象后端的演进结果。这篇文章不会去复述内核源码,而是结合近几年的实践,把交换机制的变化、压缩内存的取舍、配置方法和调优实验一起梳理清楚。适合那些手上管着云服务器、做嵌入式系统,或者只是想真正看懂 free 命令第二列的人。

1. 交换空间的老印象,为什么会过时

1.1 从“独立分区”到“任意后端”的认知转换

大多数人对 swap 的认识还停留在“给磁盘划分一个专门的交换分区,然后在 /etc/fstab 写一行”。这个习惯是从早期 Linux 使用传承下来的,因为当年内核确实需要一个明确的块设备区域来存放 swap slot。一个四等分的磁盘里,主分区、逻辑分区之外还会隔出 swap 分区;在嵌入式设备上更常见的是用一个小分区专门存放交换数据。我自己当年也是这么干的。

这套做法在 2.x 时代并没有什么问题。但后来系统内存变得很大,内存不足不再是常态,swap 却并没有因此消失。原因在于内核回收内存时,对匿名页和文件页的处理路径是不同的。文件页可以直接丢弃,因为没有经过修改的页面可以从原始文件重新读取。匿名页没有这样的源头,必须找一个地方先保存数据,否则就只能 OOM。只要程序还需要在多个进程之间分配远超过物理内存的虚拟地址空间,swap 的地位就不会动摇。

然而与此同时,内核把“可保存匿名页的地方”从一个强制分区扩展到了很多种后端。swap 文件、压缩内存块设备、缓存后端,甚至未来可能的热插拔内存,都可以被抽象成“交换空间”。所以当你看到“虚拟交换空间”这个说法时,不要以为它是指某个新的压缩文件系统,它更像是在说 swap 机制本身已经慢慢脱离了固定分区的限制。

传统部署和现代部署的差别可以用下面这张表来概括:

维度传统 swap 分区现代虚拟交换空间
后端形态磁盘分区zram、zswap、swapfile、未来持久内存
槽位映射与块设备 block 强绑定逻辑编号空间,可自由路由
换出过程基本同步写盘可压缩、可延迟、可中转
预取策略固定顺序读按后端介质调整 page-cluster

这个变化不是凭空冒出来的,而是内核为了适配越来越复杂的内存介质和负载类型所做的自然选择。物理磁盘不再是唯一可以存放匿名页的地方,那“交换空间”自然也就从物理概念上升为逻辑概念。

1.2 换出页面时,系统究竟在做什么

为了讲清楚后续的选型和调优,有必要先把一次 swap-out 的内核流程梳理一遍。内存页被回收时,内核会让映射它的页表项变成不可用,同时插入一个特殊的 swap entry。这个 entry 由两部分组成:type 字段记录它属于哪个交换区,offset 字段记录它在那个交换区中的索引位置。之后进程再访问这个页面时,会发生缺页异常,内核再根据 swap entry 找到数据所在的槽位,把页面重新加载到内存中。

传统磁盘 swap 在这个流程里最耗时的是写回:一次 swap-out 可能把脏页同步写盘,swap-in 时又是一次随机读。很多老管理员因此把 swappiness 调得特别低,就是害怕频繁进入这个“慢速通道”。可问题是,如果 swap 机制把一页的数据放到一个还在 RAM 里的压缩容器中,那个“慢速通道”就变成了一次 CPU 压缩操作和一次内存读,瞬间就能省掉整条磁盘 IO 路径。这就是压缩交换、虚拟交换这些技术对现代工作负载最大的意义:它把换页从“等磁盘”变成了“算一算”。

这里还有个容易忽视的细节:swap entry 里的 type 和 offset 只是逻辑位置,内核并不关心这块空间物理上对应在哪里。它可以是磁盘块,可以是内存池中的一个对象,可以是未来某种新介质上的一个地址。这个解耦关系就是“虚拟交换空间”的底层基础——交换区变成了一张自由的路由表,而不再是一段固定的磁盘几何结构。

2. 虚拟交换空间的抽象模型

2.1 交换槽位、设备编号与后端解耦

在具体讨论 zswap 和 zram 之前,我想先展开一下 swap 的抽象模型。最早的 swap 设备实现里,一个交换分区被切分成很多固定大小的页面槽,内核通过设备号加上块偏移来定位每一个槽。因为后端是普通的块设备,这种模型是直观的:写一个页,就是把它放到某个扇区上。

到了现代内核,情况就不一样了。拿 zram 举例,/dev/zram0 看起来是一个块设备,它有容量、能格式化、能挂载成 swap,但它的数据实际存放在内存里。内核拿到一个写请求,并不是真的往某个磁盘磁道上写数据,而是调用压缩后端,把原始页变成更小的数据对象,再存进内存池。也就是说,块设备这一层只是一个非常薄的“假接口”,真正的存储逻辑完全由内存管理子系统接管。

再拿 zswap 举例,它甚至不创建块设备。zswap 直接挂在 swap 机制的回写路径上:内核决定把某个匿名页 swap out 时,zswap 先截住这个页面,进行压缩并缓存;当缓存池容量不足时,才真正把早期压缩过的页面送到底层交换设备。相对于 zram 的“设备级虚拟化”,zswap 更像是“路径级虚拟化”。两者都让交换槽位与实际存储载体之间出现了中间层。

这种中间层带来的最大好处是,内存管理代码不再需要为每一种存储介质单独写一套回收逻辑。内核只需要面向一个抽象的“交换后端”发出请求,具体数据是落在磁盘、内存池还是未来的持久内存,由后端自己决定。这就像一个快递系统不再关心货物是坐卡车还是坐飞机,它只关心发货单上写着哪个分拣中心。

2.2 为什么要用虚拟这一层来解决问题

有人可能会问:直接在一个真实磁盘上做 swap,再让它用 SSD 的高性能,难道不够吗?在物理机时代确实够,但今天的负载类型已经不允许这么简单粗暴了。

首先,现代内存回收路径非常依赖延迟控制。当运行一个搜索引擎、数据库或 Java 服务时,瞬间的内存压力非常常见。如果所有被换出的页面都直接写 SSD,哪怕 SSD 再快,也有排队延迟。虚拟交换空间允许页面先以压缩形式留在内存里,让大多数 swap-in 变成一次低成本内存访问,而不是一次存储 IO。

其次,同一台机器上的“内存”早已不是一个均匀的大池子。NUMA 节点、CXL 扩展内存、持久内存,每一级的延迟和带宽都不同。与其把每一级介质都强行包装成一个独立 swap 设备再来手动配置优先级,不如在内核抽象层里直接区分热度和延迟要求,让页面按策略自动选择驻留位置。这个方向才是标题里“现代化交换机制”真正的野心:不再是应急补丁,而是内存分层调度的一部分。

第三,虚拟化场景给 swap 提出了一个新问题:宿主机的内存页和客户机的内存页相互嵌套,swap 发生在哪一层、由谁决策,直接影响整体 QoS。抽象层可以让宿主内核更灵活地处理“客户机内存页被换出”这类事件,而不是简单地把它看作一个进程页写入磁盘。

3. zswap 与 zram:两种压缩换页路径的取舍

3.1 zswap:压缩缓存如何接入真实交换设备

zswap 是一个内核层面的压缩缓存。它拦截即将通过 swap-out 路径离开内存的匿名页,先在内存里用压缩算法把它变小,然后存到一个压缩内存池中。这个池的大小不是无上限的,默认大约是系统内存的 20%。池满了之后,早期压缩的页面才会被真正写到底层交换设备上,也就是被“逐出”到磁盘。

注意,zswap 本身并不会让磁盘交换设备消失,它必须配置一个可用的 swap 设备作为后备。这一点非常关键:如果你开了 zswap 却完全没有配置任何 swap 分区或 swap 文件,那么当压缩池被填满时,内核没有地方释放页面,只能继续压内存,最终结果和没有 swap 的系统差不多。我在实际配置过程中经常看到这种漏配的情况,结果就是明明开了 zswap,一到内存高压期照样 OOM。

启用 zswap 也很简单。多数发行版默认已经打开,你只要确认下面几个参数处于预期状态:

cat /sys/module/zswap/parameters/enabled cat /sys/module/zswap/parameters/compressor echo zstd > /sys/module/zswap/parameters/compressor

如果你用的是传统磁盘或普通 SSD,zswap 是最低风险的选择:内存里先缓冲,写不下了再交给磁盘。它既能降低 swap-in 的时延,又不需要你删掉原来的 swap 分区。

3.2 zram:把内存本身变成一块交换盘

zram 的思路又不一样,它不是缓存,而是直接把内存当作一块块设备。加载 zram 模块后,系统里会出现 /dev/zram0;你设置它的大小、格式化为 swap,然后 swapon。写入它的页面会被内核用 zstd、lzo、lz4 等算法压缩,压缩后的那部分内存才是实际占用的内存。

因为数据从头到尾都放在内存中,zram 天然没有磁盘 IO。对低内存的笔记本电脑、小规格云主机和不带磁盘的嵌入式系统来说,它就像是一个“虚拟交换空间”:敲下 swapon 之后,free 命令会告诉你系统多了一层交换能力,实际上这块交换盘占用的还是 RAM,只是通过压缩换来了更多可用空间。

当然代价也存在。所有 swap-out / swap-in 都要经过解压步骤,压缩算法再快也是 CPU 开销;如果系统里跑的是重度计算任务,CPU 本来已经拉满,再叠加压缩开销反而可能拖慢整体性能。另外,zram 在内存本身紧张时无法把物理内存腾给别的用途,它只是把页面变小了,并没有把它们挪到另一个介质上。因此 zram 更适合那些内存小、磁盘不可靠、或者完全不想依赖磁盘 IO 的场景。

3.3 选型对比:延迟、命中率与 CPU 开销

把两者放在一起比较会更直观:

维度zswapzram
底层形态RAM 中的压缩缓存RAM 中的块设备
是否需要真实 swap 设备需要不需要
数据最终存放位置优先内存池,驱逐后才写盘一直驻留内存
内存不足时的行为可以把冷页写到底层设备只能压缩得更激进,无法外置
CPU 开销压缩/解压压缩/解压
对磁盘 IO 的影响显著减少完全消除
适用场景有磁盘后端、想减负的服务器内存很小的设备、无盘环境

我在选型时有一个粗线条的经验:如果系统一定有磁盘,优先 zswap,因为它多一条“兜底”路径;如果内存小于 4GB 而且不想在磁盘上长期做交换,那么 zram 更直接。两者的共同点是,它们都在用虚拟交换空间的方式,把页面在内存和存储介质之间的位置选择交给内核去动态处理,这比手动猜测“该把多少内存留给 page cache”要省心得多。

4. 现代负载下的交换空间控制

4.1 cgroup v2 与 per-memcg 交换限制

过去整个系统只有一个全局 swap 开关。容器时代不同了,一个节点上有几十个 workload 跑在一个内核上,一个失控容器完全可以把全局交换空间耗尽,然后把整台机器的 IO 拖垮。cgroup v2 提供的内存控制接口很好地解决了这个问题,其中和 swap 关系最直接的三个控制项是 memory.high、memory.max 和 memory.swap.max。

如果你用 systemd-run 或者容器管理器管理一组进程,可以在对应的 cgroup 目录下设置 swap 上限。举个例子,把一个 group 的物理内存限制在 2GB,同时允许它最多使用 1GB 的交换空间:

mkdir -p /sys/fs/cgroup/my-group echo 2G > /sys/fs/cgroup/my-group/memory.max echo 1G > /sys/fs/cgroup/my-group/memory.swap.max

这个组合意味着该 group 的总内存容量是 3GB,超过 2GB 开始换页,超过 3GB 就只能触发内存回收甚至 OOM。这种“按组分配交换预算”的方式让虚拟交换空间不再是一个全系统均匀分配的池子,而是可以按业务重要性动态划分的资源。生产环境里我强烈建议像配置 CPU 限额一样把 memory.swap.max 纳入日常规范,否则一个跑泄漏进程的容器就能把整台宿主机拖入交换风暴。

4.2 swap 与 overcommit 的策略配合

还有一个维度很容易被忽略:交换空间和虚拟内存 overcommit 是配合工作的。内核在多数发行版下的默认 overcommit 策略允许虚拟内存总量超过物理内存。在这种策略下,程序申请匿名内存时只要地址空间足够就会成功,真正分配物理页面时可以推迟到发生缺页。如果没有 swap,一些内存突增只能靠回收或 OOM 来解决;有了虚拟交换空间,那些短暂申请、快速释放的页面就可能被压缩交换缓存吸收,避免触发高延迟回收。

但也正因如此,不要把“swap 越大越安全”理解成线性关系。当 cgroup 或系统层面 swap 用量持续上涨,又不被回收,那往往意味着某个进程在做无界缓存,而不是正常的页面抖动。我的个人判断是:先观察 si 和 so 的数值,如果自由内存很充裕但 swap 还在持续换出,应该去查发生 swap 的进程,而不是继续调大交换空间。

4.3 针对真实负载的 swappiness 与预取调整

最后想聊聊两个经常被抄错的小参数,vm.swappiness 和 vm.page-cluster。

swappiness 控制内核回收时偏向于回收文件页还是交换匿名页。默认值是 60,数值越大越愿意换出匿名页。在我的配置里,它应该跟着交换后端走:如果用的是 zram,交换本身不碰磁盘,设置为 100 相当于把换页当成一个正常的冷数据压缩通道,反而能提高整体吞吐;如果用的是 SSD 上的传统 swapfile,我会把它调到 10 到 30,避免磁盘随机读拖慢高频路径。

vm.page-cluster 控制一次换入多少页。默认为 3,意味着内核会一次读入 2 的 3 次方也就是 8 页。这个预取逻辑在机械硬盘时代非常有效,因为连续读能抵消寻道开销。但到了 SSD 和 zram 时代,预取反而会浪费 CPU 和内存带宽。所以在虚拟交换空间的后端上,我习惯直接设成 0:

sysctl vm.page-cluster=0 sysctl vm.swappiness=100

这两个参数单独看微不足道,但组合起来就是“换出则压缩驻留、换入则按需单页”的模式,非常贴近现代化交换机制设计的初衷。

5. 如何搭建一套虚拟交换空间实测环境

5.1 用 zram 替代传统 swap 分区

搭建步骤可以直接照抄。先加载 zram 模块:

modprobe zram num_devices=1

查看系统当前可用的压缩算法:

cat /sys/block/zram0/comp_algorithm

把算法切换成兼容性和压缩速度都比较平衡的 lz4:

echo lz4 > /sys/block/zram0/comp_algorithm

给这个虚拟块设备设置大小。这里的大小是该设备可容纳的“未压缩逻辑容量”,实际占用的 RAM 会随着压缩率减小。例如想获得一个约等于物理内存 1/2 的逻辑交换区,可以先设置:

echo 2G > /sys/block/zram0/disksize

接着按普通块设备的流程初始化并启用它:

mkswap /dev/zram0 swapon -p 100 /dev/zram0

-p 100 给交换区设置了很高的优先级。如果你原系统里已经有磁盘 swap,内核会优先使用 zram,这就形成了一层“压缩内存先顶、磁盘交换兜底”的分级结构,比直接禁用旧 swap 分区更稳妥。若想在启动时自动完成配置,可以借助 systemd 的 zram-generator,核心配置大致是:

[zram0] zram-size = ram / 2 compression-algorithm = zstd

配置完成后,重启并不会让人工 swapon 失效,因为开机服务会自动按配置重建设备。

5.2 多交换区优先级与 swap 文件补充

如果在 zram 之外还想增加一个文件类型的交换后端,传统 swap 文件的方式依然可用。创建一个文件并设置好权限:

fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon -p 0 /swapfile

这里要注意优先级数值的含义:Linux 中更大的数值优先被使用。所以 zram 用 -p 100,swap 文件用 -p 0,可以让系统在内存高压时先压数据到 RAM 里的虚拟交换设备,实在撑不住才动用盘上的 swapfile。对嵌入式设备来说,这个配置意味着把“交换”整体留在了内存内部,磁盘只在极端情况下工作。

swap 文件同样需要关注文件系统的支持;btrfs 上已经可以正常创建交换文件,但建议先在小范围环境里验证稳定性和挂载选项再推广到生产。

5.3 用 vmstat、zramctl 验证效果

搭建完之后不要只看 free 命令。连续观察以下几项更能说明问题:

free -h vmstat 1 zramctl

vmstat 输出的 si(swap-in)和 so(swap-out)如果一直在动,说明系统确实在经历交换压力。用我自己的测试流程来说,会先在一个 memory.max 很小的 cgroup 里启动一个不断分配内存的程序,观察 cgroup 的 swap 用量达到上限后,进程是否被 OOM 杀掉,同时注意宿主机的整体 IO 是否被拖累。

另外值得看的是 zram 的压缩率。zramctl 会直接显示压缩后数据大小与原始数据大小的比例;如果 ratio 接近 1,说明这个负载的页面大部分是不可压缩的,那 zram 的收益就很小,不如换成传统 swap 更省心。这个信息不看统计很难拍脑袋得出,所以我建议任何新环境都先跑一段真实负载的观察再决定是否长期采用。

6. 从“交换”到“内存分层”的演进思路

6.1 交换机制正在变成内存分层的调度机制

把虚拟交换空间放到更大的图景里看,你会发现一个很有意思的趋势:过去的交换空间是内存的“临时救火队员”,现在的交换几乎成了内存分层结构的一部分。热页留在 DRAM,冷页可以压缩到 RAM 中的缓存池,或者下沉到 zram、swapfile,甚至未来的持久内存和扩展内存。内核会根据页面访问频率、设备能力、延迟预算来决定把页面放在哪一层。

也就是说,交换不再是“内存满了再救一救”的被动事件,而是内核主动管理不同延迟层级存储介质的一个通用动作。虚拟交换空间提供了一组统一逻辑上的槽位,让同样的内存页面可以在不同后端之间迁移,而不需要为每一种新介质单独实现一套内存管理。这种抽象层的价值,在系统内存类型越来越多样之后会体现得更充分。

6.2 实践者该怎么面对下一步

落到实际运维和开发里,我不建议一看到新交换机制就立刻把现有环境完全推倒。先把当前系统的 swap 构成摸清楚,再用 cgroup 隔离压力,在一个小范围内试一周,最后看压缩率、si/so 和程序响应时间的变化。这套验证周期比单纯改 swappiness 要可靠得多。

如果只是个人开发机,直接把 zram 设为主交换设备通常是最快见效的尝试。要是服务器需要长时间稳定运行,那就让 zswap 在磁盘后端前面当缓冲层。我自己在把一台老式桌面机改成 zram 优先之后,最直观的变化就是卡顿变少了,磁盘 IO 大幅下降。即便你暂时没有条件升级硬件,沿着这个方向做一次小规模改造,收获也会超出预期。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询