引言
在现代操作系统中,内存管理是内核最复杂的子系统之一。当物理内存紧张时,Linux内核需要通过交换(Swap)和压缩机制来腾出空间。本文将深入剖析Linux内核的内存压缩与交换机制,涵盖传统Swap分区、zRAM压缩内存盘、zSwap前端压缩缓存,以及kswapd回收算法的实现细节。
1. 交换空间(Swap)基础原理
1.1 交换的必要性
物理内存有限,而进程的虚拟地址空间需求不断增长。当系统物理页帧耗尽时,内核必须选择牺牲部分页面:要么丢弃可重读的页面(文件缓存),要么将匿名页面写出到磁盘交换空间。交换机制使得系统能够运行超出物理内存容量的负载组合。
1.2 交换区的组织
Linux内核将交换空间划分为Swap Area(交换区),每个交换区由若干Swap Cluster(交换簇,通常是256页,即1MB)组成。交换区的核心数据结构是swap_info_struct,记录该交换区的起始扇区、总页数、可用页数等信息。
交换空间的分配以Swap Slot(交换槽)为最小单位(通常一页,4KB)。当匿名页面被换出时,内核在交换区中找到一个空闲slot,将页面内容写入磁盘,并在页表项中标记为"不在内存",同时存储该slot的索引到PTE中作为swap_entry_t。
1.3 换入换出的页表项机制
页面换出时,PTE被设置为特殊的"交换PTE"格式:高位存储swap type(交换区索引)和swap offset(槽位偏移),低位保留访问位标记。当CPU访问该页时触发缺页异常(Page Fault),内核通过swap_entry信息从磁盘读回页面,并重新建立有效的PTE映射。
2. zRAM:内存中的压缩交换设备
2.1 传统交换的瓶颈
传统磁盘交换的最大问题是延迟高。即使使用SSD,磁盘I/O的延迟也在微秒到毫秒级,而内存访问是纳秒级。当系统处于内存压力时,频繁的磁盘交换会导致严重的性能抖动。zRAM的核心思想是用CPU换I/O——将页面压缩后存储在内存中,避免磁盘访问。
2.2 zRAM设备架构
zRAM实现为一个内存中的块设备(zram_drv.c)。每个zRAM设备维护一个zram_table_entry数组,每个条目包含:
flags:标志位(LOCKED、COMPRESSIBLE、ZERO等)handle:压缩后数据的内存指针(由zsmalloc分配器管理)element:用于存储相同元素的优化(Zero Page Dedup)
2.3 压缩流与解压流
zRAM使用多压缩流(zstrm)机制,每个流绑定一个压缩算法上下文(如LZ4、Zstd)。写入页面时,内核通过zram_bio_write_page()调用压缩流,将原始4KB页面压缩后存入zsmalloc分配的内存区域。zsmalloc是zRAM专用的内存分配器,它将固定大小的内存块划分为多个class,按对象大小减少碎片。
2.4 零页面去重
zRAM对全零页面做了特殊优化:不进行压缩分配,而是通过zram_zero_page()标记该entry为ZERO flag。页面换入时,内核直接分配一个清零的物理页映射到该entry,避免无意义的压缩解压开销。这显著提升了系统启动和大量空内存场景的效率。
3. zSwap:Swap层的前端压缩缓存
3.1 设计定位
zSwap与zRAM不同:它不是独立的交换设备,而是交换层的前端缓存(front-end cache)。被换出的页面先经过zSwap压缩存入内存池,只有当池满时才真正写出到后端磁盘交换设备。这种设计结合了内存的速度和磁盘的容量。
3.2 核心架构
zSwap的核心是zswap_entry结构和内存池(pool)。每个pool绑定一个压缩算法(通过crypto API)和一个内存分配器(通常也是zsmalloc)。页面换出时,zswap_store()将页面压缩后存入pool;当pool占用超过阈值(max_pool_percent,默认20%)时,zswap_writeback()触发回写,将最久未使用的压缩页面解压写出到后端swap。
3.3 拒绝回写优化
zSwap的一个关键特性是拒绝机制(reject filter)。对于压缩率极差的页面(例如已压缩过的二进制数据),zSwap会拒绝存入,直接交给后端swap。zswap_is_page_same_filled()检测全相同内容页面(如全零),zswap_can_accept()在压缩前决定是否接受。这些优化避免了CPU浪费。
4. kswapd与内存回收机制
4.1 回收的触发条件
Linux内存回收有两个入口:
- 直接回收(Direct Reclaim):进程分配页面时发现空闲页面低于
min_free_kbytes阈值,分配路径同步执行回收,分配进程被阻塞 - kswapd后台回收:内核守护进程周期性检查水位,在直接回收发生前主动回收
4.2 页面生命周期与LRU链表
内核通过LRU(Least Recently Used)链表管理页面活跃度。自Linux 4.8引入多级LRU(Multi-Gen LRU)之前,传统实现维护两条链表:
- Active List:最近被访问过的页面
- Inactive List:候选回收页面
页面通过mark_page_accessed()在链表间移动。kswapd从Inactive List尾部扫描,将未被引用的页面回收;仍在使用的页面会被重新提升到Active List。
4.3 水位线(Watermark)机制
每个NUMA节点维护三个水位:WMARK_MIN(紧急)、WMARK_LOW(触发kswapd)、WMARK_HIGH(充足)。当空闲页面低于WMARK_LOW时,kswapd唤醒,每轮回收直到恢复到WMARK_HIGH才休眠。WMARK_MIN是最后防线,到达此水位时所有分配者都被阻塞以预留紧急页面。
4.4 交换Token(vm_swap_token)
在多核环境下,为防止所有CPU同时触发直接回收造成抖动,内核实现了交换令牌机制。通过mm/vmscan.c中的swap_token,轮流选择一个进程获得"回收豁免权",避免大量进程同时陷入直接回收的惊群效应。
5. 压缩算法实现与选择
5.1 内核压缩算法接口
Linux内核通过crypto API提供压缩能力。zRAM和zSwap通过crypto_alloc_comp()获取压缩上下文。支持的算法包括:
- LZ4:压缩速度极快(多GB/s),压缩率低(~2.1:1),适合延迟敏感
- LZO/RLE:平衡速度与支持广泛
- Zstd:现代算法,压缩率高(~2.8:1),解压快,适合高压缩率场景
- 842:硬件加速友好,中速中压缩率
5.2 算法选择的性能权衡
zRAM的压缩算法可通过/sys/block/zram0/comp_algorithm动态切换。选择策略取决于工作负载:
- 桌面/服务器:Zstd提供最佳内存节省
- 嵌入式/低延迟:LZ4最小化CPU开销
- 大数据/I/O密度:LZO平衡吞吐与压缩率
6. 性能调优与监控
6.1 关键sysctl参数
- vm.swappiness(默认60):值越高,内核越倾向于换出匿名页面而非丢弃缓存;0表示禁用匿名页面换出,100表示积极换出
- vm.vfs_cache_pressure(默认100):控制dentry/inode缓存的回收倾向
- vm.min_free_kbytes:紧急保留页面数,建议为物理内存的1-1.5%
- vm.watermark_scale_factor:控制kswapd回收的积极性(页数 = factor × zone_mem / 10000)
6.2 zRAM配置优化
现代Linux发行版(如Fedora 33+)默认启用zRAM作为交换设备而非传统swap分区。配置示例:
systemd-tmpfiles --create /etc/tmpfiles.d/zram.conf
# 或使用zram-generator
echo "zram0" > /sys/class/zram-control/hot_add
echo 4G > /sys/block/zram0/disksize
mkswap /dev/zram0
swapon /dev/zram0 -p 100
6.3 监控与诊断工具
- vmstat -s:查看交换统计(页面换入/换出速率)
- sar -W:历史交换活动
- cat /proc/swaps:查看交换区配置与使用情况
- zramctl:监控zRAM设备压缩率、内存用量
- /sys/kernel/mm/zswap/:zSwap统计(stored_pages、pool_total_size、reject_*)
- perf trace -e mm_vmscan_*:追踪kswapd回收事件
7. 实战案例:内存压力诊断
7.1 抖动问题排查
当系统出现"交换抖动"(频繁换入换出)时,通常表现为:
- CPU iowait升高,磁盘利用率持续高位
- 进程响应延迟急剧增加
- kswapd进程CPU占用持续上升
诊断步骤:vmstat 1观察si/so(swap in/out)是否持续大于0;若so占多数,说明匿名页面频繁换出,应增大内存或启用zRAM;若si占多数,说明换出的页面被频繁访问,内存配置明显不足。
7.2 zRAM vs zSwap的选择
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 桌面笔记本 | zRAM | 避免磁盘磨损,响应快 |
| 单Swap分区不变更 | zSwap | 前端缓存,透明加速 |
| 嵌入式小内存 | zRAM大disksize | 最大化可用内存 |
| 数据库服务器 | Swap分区+禁用匿名换出 | 数据库自有缓存管理 |
8. 最新发展趋势
- Multi-Gen LRU(Linux 6.1+):基于多代年龄的页面取代传统Active/Inactive双链表,减少"在使用但未被扫描到"的误回收
- 水位自适应(Linux 6.6+):根据页面类型和回收历史动态调整水位阈值
- zRAM WRITEBACK:允许将zRAM中不常用的压缩页面写出到磁盘,扩展zRAM的容量利用率
- PSI(Pressure Stall Information):精确量化的内存压力监控,替代传统的
vmstat经验观察
总结
Linux内核的内存压缩与交换机制是一个精密的分层系统:从底层的Swap空间组织,到zRAM的内存压缩设备,再到zSwap的前端缓存,以及kswapd与多级LRU的回收协同。理解这些机制不仅能帮助我们正确调优系统性能,还能在内存不足场景下快速诊断瓶颈。在SSD时代,zRAM与zSwap通过"用CPU换延迟"的策略,已成为现代Linux内存管理不可或缺的一环。

发表评论 取消回复