)
11. Swap交换分区机制实现Swap交换分区或交换文件是Linux内核的“虚拟内存扩展”当系统物理内存不足时内核会把不活跃的匿名页换出到swap分区释放物理内存当进程再次访问这些被换出的页时内核会把页从swap分区换入到物理内存。很多工程师对swap的认知停留在“扩展内存”的表层却不理解swap的底层实现、换入换出的全流程、swap分区与交换文件的区别、swap的性能优化最终导致系统出现swap抖动、性能下降、OOM误杀等问题。本章节基于Linux 6.6 LTS内核完整拆解swap的核心设计、数据结构、换入换出全流程、工程调优最佳实践。11.1 Swap的核心设计目标扩展虚拟内存当系统物理内存不足时用磁盘空间模拟物理内存让系统可以运行总虚拟内存超过物理内存大小的进程内存复用把不活跃的匿名页换出到swap分区释放物理内存给活跃的进程使用最大化物理内存利用率透明性swap的换入换出对用户态进程完全透明进程不需要知道自己的页是否被换出到swap性能优化通过swap预读、批量换入换出、swap缓存等机制减少swap IO的次数提升swap的性能。11.2 Swap的核心数据结构11.2.1 Swap分区与交换文件的统一抽象struct swap_info_structLinux内核用struct swap_info_struct统一抽象swap分区和交换文件每个swap设备分区或文件对应一个swap_info_struct实例定义在include/linux/swap.h中核心字段拆解struct swap_info_struct { // swap设备的类型SWP_FILE交换文件或SWP_BLKDEV交换分区 unsigned int flags; // swap设备的优先级范围-1~32767值越大越优先使用 int prio; // swap设备的总槽位数每个槽位对应一个4KB页 unsigned long max; // swap设备的已用槽位数 unsigned long inuse_pages; // swap设备的空闲槽位位图 unsigned long *swap_map; // swap设备的锁 spinlock_t lock; // 交换文件对应的file结构体交换分区为NULL struct file *swap_file; // 交换分区对应的block_device结构体交换文件为NULL struct block_device *bdev; // swap缓存的address_space struct address_space *swap_space; // 换入换出的统计信息 unsigned long in_pages; unsigned long out_pages; };核心字段深度解析prioswap设备的优先级用户可以通过swapon -p 设置值越大越优先使用如果优先级相同内核会轮询使用max/inuse_pagesswap设备的总槽位数和已用槽位数每个槽位对应一个4KB页所以swap设备的总大小是max * 4KBswap_map空闲槽位的位图每个bit对应一个槽位0表示空闲1表示已用swap_spaceswap缓存的address_space和文件页的address_space类似用于缓存从swap分区换入的页避免重复的swap IO。11.2.2 Swap入口地址swp_entry_t当一个匿名页被换出到swap分区时内核会把该页的页表项替换为swap入口地址swp_entry_tswap入口地址包含两个信息swap设备的编号SWP_TYPE_SHIFT位对应swap_info数组的索引swap设备的槽位号SWP_OFFSET_SHIFT位对应swap设备内的槽位号。当进程再次访问该页时CPU会触发缺页异常内核从页表项中提取swap入口地址找到对应的swap设备和槽位把页从swap分区换入到物理内存。11.3 Swap的核心操作流程11.3.1 Swap的换出流程内存回收时触发Swap的换出流程是在内存回收时触发的当文件页回收的数量不够且swap充足时内核会回收匿名页换出到swap分区完整执行流程内存回收时选择一个匿名页准备换出↓1. 检查页是否可以换出├→ 页是否被锁定PG_locked如果是跳过├→ 页是否有多个映射_mapcount 0如果是通过反向映射清除所有页表项└→ 页是否是脏页如果是不需要回写匿名页没有后备文件↓2. 分配swap槽位├→ 遍历所有swap设备按优先级从高到低├→ 找到一个有空闲槽位的swap设备├→ 从swap设备的swap_map位图中找到一个空闲槽位├→ 标记该槽位为已用└→ 生成swap入口地址swp_entry_t↓3. 把页写入swap分区├→ 调用address_space-a_ops-writepage()提交IO请求到块层├→ 把页加入到swap缓存的address_space-i_pages XArray中├→ 设置页的PG_swapcache标志标记为swap缓存页└→ 等待IO完成可选内存回收时通常异步等待↓4. 修改页表项├→ 把页表项替换为swap入口地址swp_entry_t├→ 设置页表项的P位为0页不存在└→ 刷新TLB↓5. 释放物理页├→ 清除页的PG_dirty/PG_active/PG_inactive标志├→ 从MGLRU链表中移除└→ 释放回伙伴系统核心工程细节swap缓存的作用如果多个进程共享同一个匿名页fork之后COW之前换出到swap分区后再次访问时不需要重复从swap分区读取直接从swap缓存中获取提升性能异步换出内存回收时通常异步提交IO请求不等待IO完成继续回收其他页提升回收效率swap槽位的释放当页从swap分区换入到物理内存后swap槽位不会立即释放而是保留在swap缓存中直到页被修改COW或者swap缓存被回收。11.3.2 Swap的换入流程缺页异常时触发Swap的换入流程是在缺页异常时触发的当进程访问的虚拟地址对应的页表项是swap入口地址时内核会触发swap缺页异常把页从swap分区换入到物理内存完整执行流程CPU触发缺页异常内核检查页表项发现是swap入口地址↓1. 从页表项中提取swap入口地址swp_entry_t找到对应的swap设备和槽位↓2. 查找swap缓存├→ 以swap入口地址为key在swap设备的swap_space-i_pages XArray中查找├→ 如果页已经在swap缓存中且PG_uptodate1直接跳到步骤5从swap缓存中获取页└→ 如果页不在swap缓存中或者PG_uptodate0进入步骤3从swap分区读取↓3. 分配物理页├→ 调用alloc_page()分配一个空白物理页├→ 把页加入到swap缓存的address_space-i_pages XArray中├→ 设置页的PG_locked和PG_swapcache标志↓4. 从swap分区读取页├→ 调用address_space-a_ops-readpage()提交IO请求到块层├→ 等待磁盘IO完成中断处理函数唤醒等待的进程├→ IO完成后设置页的PG_uptodate标志清除PG_locked标志↓5. 建立页表映射├→ 把页表项替换为物理页的地址├→ 设置页表项的权限位可读、可写、用户态可访问├→ 刷新TLB↓6. 处理swap槽位├→ 如果页是只读的多个进程共享保留swap槽位下次访问时直接从swap缓存获取└→ 如果页是可写的释放swap槽位清除swap_map位图中的对应bit↓7. 把页加入到MGLRU链表的当前max_gen代↓8. 缺页异常处理完成CPU重新执行触发异常的指令核心工程细节swap预读和文件预读类似内核会根据进程的访问模式预读后续的swap槽位到swap缓存中减少swap IO的次数提升性能swap缺页异常的延迟swap缺页异常需要访问磁盘延迟是次缺页异常的上千倍是系统内存抖动的核心来源swap槽位的复用swap槽位释放后会被重新分配给其他需要换出的页swap设备的空间可以循环利用。11.4 Swap的工程实践与避坑指南1.Swap分区与交换文件的选型Linux支持两种swap设备swap分区和交换文件两者的核心区别特性Swap分区交换文件性能略高直接访问块设备不需要经过文件系统略低需要经过文件系统有额外的开销灵活性低创建/删除需要重新分区重启系统高创建/删除只需要文件操作不需要重启系统安全性高直接访问块设备不容易被误删低存储在文件系统中可能被误删适用场景生产环境性能要求高的场景临时场景测试环境需要快速扩展swap的场景最佳实践生产环境优先使用swap分区性能略高安全性好如果需要快速扩展swap可以临时创建交换文件后续再替换为swap分区。Swap的创建与配置1.创建swap分区# 1. 创建一个新的分区类型为82Linux swap fdisk /dev/sdb # 2. 格式化swap分区 mkswap /dev/sdb1 # 3. 启用swap分区设置优先级为10 swapon -p 10 /dev/sdb1 # 4. 永久启用编辑/etc/fstab添加以下行 /dev/sdb1 swap swap defaults,pri10 0 02.创建交换文件# 1. 创建一个8GB的交换文件块大小为1GB共8块 dd if/dev/zero of/swapfile bs1G count8 # 2. 设置交换文件的权限为600只有root可以读写 chmod 600 /swapfile # 3. 格式化交换文件 mkswap /swapfile # 4. 启用交换文件设置优先级为5 swapon -p 5 /swapfile # 5. 永久启用编辑/etc/fstab添加以下行 /swapfile swap swap defaults,pri5 0 03.查看swap配置# 查看swap设备的信息 swapon --show # 查看swap的使用情况 free -h cat /proc/meminfo | grep Swap2.Swap的性能优化使用SSD作为swap设备SSD的IOPS和吞吐量远高于机械硬盘swap的性能会提升几十倍创建多个swap分区分布在不同的SSD上内核会轮询使用优先级相同的swap分区提升swap的IO带宽调大page-cluster参数机械硬盘调大到532页合并更多的swap IO提升性能SSD调小到01页减少不必要的IO调小swappiness参数数据库等延迟敏感的服务设置为0禁止回收匿名页避免swap缺页异常使用swap缓存swap缓存是默认启用的不需要额外配置能有效减少swap IO的次数避免swap抖动最根本的方法是扩容物理内存让进程的工作集小于可用物理内存如果无法扩容调小swappiness调大min_free_kbytes提前触发kswapd异步回收。3.Swap的禁用与删除临时禁用swap# 禁用所有swap设备 swapoff -a # 禁用指定的swap设备 swapoff /dev/sdb1永久禁用swap# 编辑/etc/fstab删除或注释掉swap相关的行 # 临时禁用swap swapoff -a删除交换文件# 1. 禁用交换文件 swapoff /swapfile # 2. 删除交换文件 rm /swapfile # 3. 编辑/etc/fstab删除交换文件相关的行删除swap分区# 1. 禁用swap分区 swapoff /dev/sdb1 # 2. 删除swap分区 fdisk /dev/sdb # 3. 编辑/etc/fstab删除swap分区相关的行4.避坑指南不要在机械硬盘上创建过大的swap分区机械硬盘的IO性能很差过大的swap分区会导致系统出现严重的swap抖动不要把swap分区和数据分区放在同一个磁盘上swap IO和数据IO会竞争磁盘带宽导致两者的性能都下降不要在生产环境频繁启用/禁用swap频繁启用/禁用swap会导致系统的内存管理出现混乱甚至触发OOM不要把swappiness设置为0后完全依赖物理内存如果物理内存不足系统会直接触发OOM杀死进程没有任何缓冲建议设置为10优先回收文件页保留少量的swap作为缓冲。