11. Swap交换分区机制实现
11.1 Swap的核心设计目标
- 扩展虚拟内存:当系统物理内存不足时,用磁盘空间模拟物理内存,让系统可以运行总虚拟内存超过物理内存大小的进程;
- 内存复用:把不活跃的匿名页换出到swap分区,释放物理内存,给活跃的进程使用,最大化物理内存利用率;
- 透明性:swap的换入换出对用户态进程完全透明,进程不需要知道自己的页是否被换出到swap;
- 性能优化:通过swap预读、批量换入换出、swap缓存等机制,减少swap IO的次数,提升swap的性能。
11.2 Swap的核心数据结构
11.2.1 Swap分区与交换文件的统一抽象:struct swap_info_struct
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; };- prio:swap设备的优先级,用户可以通过swapon -p 设置,值越大,越优先使用;如果优先级相同,内核会轮询使用;
- max/inuse_pages:swap设备的总槽位数和已用槽位数,每个槽位对应一个4KB页,所以swap设备的总大小是max * 4KB;
- swap_map:空闲槽位的位图,每个bit对应一个槽位,0表示空闲,1表示已用;
- swap_space:swap缓存的address_space,和文件页的address_space类似,用于缓存从swap分区换入的页,避免重复的swap IO。
11.2.2 Swap入口地址:swp_entry_t
- swap设备的编号:SWP_TYPE_SHIFT位,对应swap_info数组的索引;
- swap设备的槽位号:SWP_OFFSET_SHIFT位,对应swap设备内的槽位号。
11.3 Swap的核心操作流程
11.3.1 Swap的换出流程(内存回收时触发)
- swap缓存的作用:如果多个进程共享同一个匿名页(fork之后COW之前),换出到swap分区后,再次访问时,不需要重复从swap分区读取,直接从swap缓存中获取,提升性能;
- 异步换出:内存回收时,通常异步提交IO请求,不等待IO完成,继续回收其他页,提升回收效率;
- swap槽位的释放:当页从swap分区换入到物理内存后,swap槽位不会立即释放,而是保留在swap缓存中,直到页被修改(COW),或者swap缓存被回收。
11.3.2 Swap的换入流程(缺页异常时触发)
- swap预读:和文件预读类似,内核会根据进程的访问模式,预读后续的swap槽位到swap缓存中,减少swap IO的次数,提升性能;
- swap缺页异常的延迟:swap缺页异常需要访问磁盘,延迟是次缺页异常的上千倍,是系统内存抖动的核心来源;
- swap槽位的复用:swap槽位释放后,会被重新分配给其他需要换出的页,swap设备的空间可以循环利用。
11.4 Swap的工程实践与避坑指南
1.Swap分区与交换文件的选型
特性 | Swap分区 | 交换文件 |
性能 | 略高,直接访问块设备,不需要经过文件系统 | 略低,需要经过文件系统,有额外的开销 |
灵活性 | 低,创建/删除需要重新分区,重启系统 | 高,创建/删除只需要文件操作,不需要重启系统 |
安全性 | 高,直接访问块设备,不容易被误删 | 低,存储在文件系统中,可能被误删 |
适用场景 | 生产环境,性能要求高的场景 | 临时场景,测试环境,需要快速扩展swap的场景 |
最佳实践:生产环境优先使用swap分区,性能略高,安全性好;如果需要快速扩展swap,可以临时创建交换文件,后续再替换为swap分区。
Swap的创建与配置
1.创建swap分区:
# 1. 创建一个新的分区,类型为82(Linux 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,pri=10 0 02.创建交换文件:
# 1. 创建一个8GB的交换文件,块大小为1GB,共8块 dd if=/dev/zero of=/swapfile bs=1G count=8 # 2. 设置交换文件的权限为600,只有root可以读写 chmod 600 /swapfile # 3. 格式化交换文件 mkswap /swapfile # 4. 启用交换文件,设置优先级为5 swapon -p 5 /swapfile # 5. 永久启用,编辑/etc/fstab,添加以下行 /swapfile swap swap defaults,pri=5 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参数:机械硬盘调大到5(32页),合并更多的swap IO,提升性能;SSD调小到0(1页),减少不必要的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作为缓冲。