Linux关闭swap后如何防止内核I/O抖动?
问题分析与解决方案
首先得戳破你遇到的核心误区:关闭swap或设置vm.swappiness=0,只控制Linux内核对「匿名页」(也就是进程的堆、栈这类没有磁盘文件对应的数据页)的交换行为,但对「文件映射页」(比如程序的可执行代码、动态库这类本来就来自磁盘的页)完全无效。
你看到的大量磁盘读操作,本质上不是swap交换,而是内核在内存不足时回收了代码页(因为这些页可以随时从磁盘重新读取),之后程序执行到这些代码时又得重新从磁盘加载,反复循环导致的。这也是为什么关闭swap后问题没改善——swap管的是匿名页的交换,而你面对的是代码页的回收与重读。
具体原因拆解
你的故障进程因为bug生成大量线程,每个线程都会占用自己的栈空间(属于匿名页),这些匿名页会快速耗尽系统内存。当内存不足时,内核的页回收机制会优先选择回收「可回收的页」,而代码页就是典型的可回收页(因为磁盘上有原文件)。内核把这些代码页从内存里清出去,给新的线程栈腾空间;但当进程需要执行这些代码时,又必须从磁盘把它们读回来,这就导致了iotop里看到的持续磁盘读操作。
解决方案
1. 根本解决:修复程序的线程泄漏bug
这是唯一能彻底解决问题的办法:
- 排查代码中线程创建的逻辑,比如是否有线程没有正确调用
join或detach,导致线程资源无法释放; - 检查线程池的配置,是否存在无限创建线程的逻辑;
- 用
pstack <pid>或htop工具查看进程的线程列表,定位哪些线程在无意义地存活。
2. 临时缓解:限制进程的线程数量
通过限制进程能创建的最大线程数,避免内存被无限耗尽,让故障进程更早触发错误而崩溃:
- 临时生效:在终端执行
ulimit -u 1024(这里的1024是最大线程数,可根据服务器配置调整),之后启动的进程都会受到这个限制; - 永久生效:编辑
/etc/security/limits.conf,添加如下配置(替换your_username为进程运行的用户):
重启系统或重新登录后生效。your_username hard nproc 1024 your_username soft nproc 512
3. 调整内核参数,让内核优先杀死故障进程而非回收代码页
既然你希望故障程序提前崩溃,而非让内核反复回收代码页,可以调整以下参数:
vm.vfs_cache_pressure:这个参数控制内核回收文件映射页(代码页属于这类)的倾向,默认值是100。设置为0时,内核会尽量保留文件页,优先回收匿名页;当匿名页也不足时,会直接触发OOM Killer杀死进程:# 临时生效 sysctl -w vm.vfs_cache_pressure=0 # 永久生效,写入/etc/sysctl.conf echo "vm.vfs_cache_pressure=0" >> /etc/sysctl.conf sysctl -pvm.min_free_kbytes:设置系统的最低空闲内存阈值,当内存低于这个值时,内核会更积极地触发OOM Killer而非回收页。根据服务器内存大小调整,比如16G内存可以设为64M:sysctl -w vm.min_free_kbytes=65536 echo "vm.min_free_kbytes=65536" >> /etc/sysctl.conf sysctl -p
4. 配置OOM Killer优先杀死故障进程
默认OOM Killer会根据进程的内存占用、优先级等选择要杀死的进程,你可以给故障进程设置更高的OOM分数,让它被优先杀死:
- 找到故障进程的PID,执行:
(echo 1000 > /proc/<pid>/oom_score_adjoom_score_adj的范围是-1000到1000,值越大,OOM Killer越优先选择该进程)
内容的提问来源于stack exchange,提问作者Torsten Bronger
相关产品推荐
相关产品推荐

