我因为kswapd0占用cpu 100%问题查了很多资料,尝试了各种方案,大部分参考价值不大,下面是我最终的建议。
CPU占用高直接原因
kswapd0正尝试将使用频率不高的程序数据移动到swap,此过程占用CPU极高。耗时几分钟到几十分钟。
间接原因
某个进程申请内存过多,系统内存不足,触发kswapd0转移内存数据腾出空间
根本原因
占用内存过多的进程是否真的应该占用这么多内存?有几个可能的原因:
- 在内存较小的计算机,打开需要较多内存的应用:比如打开非常多网页,或打开CAD,PS,虚拟机,非常大的文件等。
- 该程序有内存泄露(或不合理使用内存),会线性不断申请更多内存
- 感染病毒,伪装为kswapd0进程,非root进程,可通过"killall -9 kswapd0"杀死
- linux内核bug,某些情况会触发(这里有个2016年的帖子讨论相关bug,已在2022年修复)
更合理的解决方案?
遇到内存不足时,一般有2种措施可选:
- 使用kswapd0腾空闲置数据,释放更多内存
- 杀死占用内存最多的进程
遇到内存不足时,在windows上,一般是kill掉占用内存最多的进程,所以有时候会遇到OOM错误,但不会出现类似kswapd0导致的系统卡死。 而linux考虑到OOM终止进程可能造成大量数据丢失或损坏,所以linux默认会使用kswapd0腾出更多内存空间。
但计算机硬件内存始终是有限的,释放内存可能有帮助,也可能没有帮助。更多时候会导致长达十几分钟CPU高占用系统卡死。
在我遇到的问题里,我使用llm生成的banbot量化策略代码自动编译执行回测,它大部分情况生成的代码都不错,但偶尔生成的代码会导致内存泄露。目前我已通过调整提示词和内置检查来避免。不过llm生成代码越来越多,因代码缺陷导致内存泄露可能会更加常见。
所以我感觉发现内存不足时,杀死申请内存最多的进程或许也是一种不错的方案。下面是一个sh脚本,它应该在crontab中配置为每分钟运行:
- 检查当前是否存在kswapd0进程
- 如果kswapd0进程不是root用户,直接kill掉
- 如果kswapd0进程是root用户,且CPU占用率超过70%,找到占用内存最大的进程,kill掉
在决定使用此脚本前,请先排查明确您导致此问题的根本原因,如果确认符合您的场景才可使用
#!/bin/bash
# https://github.com/anyongjin/linux_oom_killer
# mail anyongjin163@163.com
# Function to log with timestamp
log_with_timestamp() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Function to check and possibly kill kswapd0 and the highest memory usage process
check_and_kill() {
# Check if kswapd0 process exists
kswapd_pid=$(pgrep -x kswapd0)
if [ -z "$kswapd_pid" ]; then
exit 0
fi
# Check if current user is root
if [ "$EUID" -ne 0 ]; then
# Non-root user, directly attempt to kill kswapd0 (Note: kernel processes cannot be killed, this operation may fail)
kill -9 "$kswapd_pid" 2>/dev/null
if [ $? -eq 0 ]; then
log_with_timestamp "kswapd0 process killed (non-root user)."
else
log_with_timestamp "Failed to kill kswapd0 (likely due to permissions or kernel process)."
fi
return
fi
# For root user, check kswapd0's CPU usage
# Use top with shorter interval for faster but still accurate reading
# Run top for 1 second instead of 2 seconds
cpu_usage=$(top -b -n 2 -d 0.5 -p "$kswapd_pid" | tail -n 1 | awk '{print int($9)}')
if [ "$cpu_usage" -gt 10 ]; then
log_with_timestamp "kswapd0 CPU usage is ${cpu_usage}% (threshold: 70%), $([ "$cpu_usage" -gt 70 ] && echo "taking action..." || echo "no action needed.")"
fi
if [ "$cpu_usage" -gt 70 ]; then
# Find the process with the highest memory usage (exclude header row, using ps aux --sort=-%mem)
max_mem_pid=$(ps aux --sort=-%mem | awk 'NR==2 {print $2}')
if [ -n "$max_mem_pid" ]; then
kill -9 "$max_mem_pid"
log_with_timestamp "Killed process $max_mem_pid with highest memory usage."
else
log_with_timestamp "No processes found to kill."
fi
fi
}
# Main script logic
# Check for the correct number of arguments
if [ "$#" -ne 2 ]; then
log_with_timestamp "Usage: $0 <number_of_runs> <interval_in_seconds>"
exit 1
fi
# Assign command line arguments to variables
runs=$1
interval=$2
# Loop to run the check_and_kill function the specified number of times
for (( i=1; i<=$runs; i++ )); do
check_and_kill
# Sleep for the specified interval, unless it's the last run
if [ $i -lt $runs ]; then
sleep "$interval"
fi
done
授予执行权限:chmod +x oom_killer.sh
然后在crontab -e中配置此脚本10s运行一次(每分钟启动1次,启动后间隔9s执行6次检查,每次检查耗时1s)
* * * * * /root/oom_killer.sh 6 9 >> /var/log/oom_killer.log 2>&1
为了尽可能方便使用,我写了一个自动安装脚本,您可执行下面命令:
curl -sL https://banbot.site/set_oom_killer.sh | sudo bash
或者您可通过[github仓库]查看更详细信息。