当前位置:首页 >  IDC >  服务器 >  正文

进程被 OOM Kill:dmesg 定位与关键进程保护

 2026-09-05 11:42  来源: 互联网   我来投稿 撤稿纠错

  一键部署OpenClaw

有一种故障特别难查:服务莫名其妙消失了,应用日志里什么都没留下,没有崩溃堆栈,没有异常退出记录。进程就这么凭空不见。碰到这种情况,十有八九是内核干的——内存不够了,OOM Killer 挑中它杀了。

OOM 的全称是 Out of Memory,它是内核的最后手段。Linux 默认采用乐观的内存分配策略,进程申请内存时内核只是答应下来,并不真的预留物理内存。等到所有进程都真要用内存了,发现不够,内核没别的办法,只能挑一个进程杀掉来腾地方。杀谁由一套打分机制决定。

这套打分有个反直觉的地方:它不保证杀掉占用最大的那个。评分会考虑进程的内存占用、运行时长、是不是 root 启动的、以及管理员设定的 oom_score_adj。我见过 sshd 被杀而失控的 Java 进程安然无恙的情况——因为后者被设过保护值。所以别想当然,去看日志。

确认是不是 OOM 的手段就一条:查内核日志。dmesg 里会有明明白白的一行,写着 Out of memory: Kill process,后面跟着进程号、进程名、评分,再后面是被杀进程当时占了多少内存。有这一行,案子就破了。

#!/bin/bash
# OOM 排查与关键进程保护

echo "=== 1. 确认是不是 OOM 杀的 ==="
dmesg -T 2>/dev/null | grep -i "killed process" | tail -n 10
journalctl -k 2>/dev/null | grep -i "out of memory" | tail -n 10

echo "=== 2. 看当前各进程的 OOM 评分,谁最危险 ==="
ps -eo pid,comm,oom,oomadj,rss --sort=-oom 2>/dev/null | head -n 15

echo "=== 3. 保护关键进程:sshd / mysqld / nginx ==="
for svc in sshd mysqld nginx; do
pid=$(pidof -s "$svc" 2>/dev/null)
if [ -n "$pid" ]; then
echo -500 > "/proc/$pid/oom_score_adj" 2>/dev/null \
&& echo "已保护 $svc (pid=$pid) oom_score_adj=-500"
fi
done

oom_score_adj 的取值范围是 -1000 到 1000。设成 -1000 等于告诉内核这个进程永远别杀,设成 1000 等于主动报名当炮灰。关键服务给负值,可牺牲的后台任务给正值,让内核在关键时刻有得选,不至于把数据库和 SSH 一起端掉。

# 持久化保护:写进 systemd 单元,别用命令行(重启就丢)

# 方式一:直接改服务文件
# /etc/systemd/system/mysqld.service.d/oom.conf
[Service]
OOMScoreAdjust=-500

# 方式二:推荐用 drop-in,不动原始文件
# systemctl edit mysqld 然后写入同样的内容

# 更进一步的主动限制:给服务设内存上限
# 达到 MemoryHigh 会被限流,达到 MemoryMax 才被杀
# 比等系统级 OOM 精准得多
[Service]
MemoryHigh=2G
MemoryMax=2500M

# 改完执行:
# systemctl daemon-reload
# systemctl restart mysqld

有人说既然 OOM 这么坑,干脆把它关掉。千万别。关掉之后内存耗尽时内核没有退路,要么直接 panic,要么整个系统卡死不响应,比杀掉一个进程惨烈得多。正确的思路是让内核杀对人,而不是不让它杀。

更根本的解法是别让内存走到那一步。给容易失控的服务设 systemd 的 MemoryMax,让它在自己的笼子里先被限制住;适当加一点 swap 作为缓冲,给系统留出反应时间;真不够了就扩容。OOM 保护是最后一道保险,平时该做的是别让这道保险被触发。

相关阅读:OOM Kill 是系统层的最后一道防线。《服务器故障排查总纲:按现象反查的五层定位法》按现象反查 24 种故障,内存被谁吃掉、进程为什么消失,从总纲定层到本篇定位,一条线走完。

申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

相关标签
网站服务器
服务器

相关文章

  • Linux 磁盘满排查:df 到 du,定位大文件与日志轮转

    网站突然写不进数据,上传图片报失败,数据库开始拒绝写入。登上机器df一看,使用率100%。这种故障不复杂,但它有个特别坑的地方:你按常规思路用du去找大文件,把所有目录的大小加起来,发现远远小于磁盘用量。两个数对不上。对不上通常有三个原因。第一个最常见:文件被删了,但还被某个进程占着。Linux下删

    标签:
    网站服务器
  • MySQL 慢查询拖垮 CPU:processlist 到 pt-query-digest

    服务器CPU突然打满,网站慢得像拨号上网。登上机器一看,MySQL占了大头。这时候最忌讳的是直接重启数据库——重启完负载确实掉了,但过不了多久它还会爬回来,因为那条慢SQL还在那儿等着被执行。第一步是抓现行。SHOWPROCESSLIST能看到此刻正在跑的语句,但默认只显示前100个字符,长SQL会

  • MySQL 1040 Too many connections:连接泄漏定位与上限设定

    网站隔三差五报一句数据库连接失败,刷新两下又好了。这种时好时坏的毛病最烦人,因为它不像宕机那样干脆,你没法守在屏幕前等它出现。等到真去查的时候,现场早没了。MySQL从5.7起max_connections默认就是151,8.0也沿用了这个值。超过之后新连接直接被拒,报ERROR1040。看到这个错

  • PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算

    间歇性502里最磨人的一种:低峰期一切正常,流量一上来就崩,过会儿又自己好了。FPM日志里翻出serverreachedpm.max_children,基本就能确诊——子进程开到上限,新请求没进程可用了。很多教程看到这条日志就让你把pm.max_children调大,却不说调到多少。拍脑袋填个100

  • PHP 500 错误定位四步:日志、开关、堆栈、复现

    500和502的差别,一句话说清:502是Nginx拿不到上游响应,500是PHP自己跑着跑着崩了,然后把这个错误码一路传回浏览器。所以500的现场一定在PHP侧,去Nginx的error.log里翻是找不到的。第一步,确认错误到底出在哪一层。curl拿状态码只是看到表象,真正的判断依据是响应头里有

热门排行

信息推荐