Linux OOM机制

记录一次程序总是异常中止的解决过程

问题描述:

开发的爬虫脚本在服务器上99%的时间都可以正常运行,但是偶尔会自动中止,进程消失。频率不固定,有的时候一天一次,有的时候一周一次,甚至很久不报错。后来解决该问题后,发现其实就是因为触发了某些条件才会出现异常,所以并不是有规律可言的。

解决过程

前面的这些解决过程不是很重要,实际是Linux系统的OOM机制导致程序被杀,不过遇到问题一般很难一开始就精准的定位到问题,都是一步步的推测问题产生的原因,然后不断地排除,直至问题的解决。所以前面这些步骤仅是在问题解决时的一些尝试性思路。

增加更详细的日志&添加异常捕获

因为异常发生是低概率事件,正常调试难以还原异常产生过程,所以只能在一些比较重要的位置添加日志打印,来帮助判断程序走到了哪里。

在比较核心的位置外层加上异常捕获,避免部分操作异常直接导致程序结束。

尝试定位问题数据

经过上述操作后,又观察了两周,程序每次结束时并没有捕获到任何异常,不过根据日志定位到问题大概率出在PDF解析这一步骤。并且也定位到了问题数据,也就是每次程序结束之前爬取的数据,数据的基本特征就是PDF页数特别多,几百页

针对问题数据进行重复测试

一开始的想法就是既然找到了问题数据,直接拿过来调整一下,看看到底是什么问题(可能是之前未考虑到的情况,程序不能正常处理),然而经过测试却可以正常运行并且解析,在Windows系统下单条数据跑了很久都没问题。

在服务器测试

这个时候考虑实际运行环境为服务器(centos),就开始在服务器上重复跑问题数据。一开始也是可以正常运行的,但是每次跑几分钟到半小时不等,就会自动结束。

并且考虑到nohup在部分情况下不能抛出异常的问题,是采用非后台运行的方式 启动的,奇怪的是这样也没有抛出任何异常,反正就是莫名其妙的终止了,并且经过不断的重复测试,发现每次父进程都会一直在,子进程没了,然后在结束的时间点,会多出另一个子进程。

考虑是程序异常结束时下载的文件不正确

每次将下载的文件都保存下来,程序结束时查看本次下载的文件是正常完整的PDF,并非是文件异常导致的进程结束。

考虑是PDFtools未能抛出异常

因为程序是在PDF解析的时候出问题,且在爬虫程序中没有捕获到异常,所以考虑是不是PDFtools中遇到特殊数据出错,且未能抛出,但是经过实际测试,可以正常抛出异常。

考虑是liunx自动杀掉了程序

排除掉了所有程序可能存在的问题,并且在windows正常的情况下,能想到的点就是服务器会杀程序,所以查了下Linux在什么情况下会杀进程。

OOM机制

Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉。内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory()被触发,然后调用select_bad_process()选择一个”bad”进程杀掉。

如何判断和选择一个”bad进程呢?linux选择”bad”进程是通过调用oom_badness(),挑选的算法和想法都很简单很朴实:最bad的那个进程就是那个最占用内存的进程。

参考文章:https://blog.csdn.net/lovoo/article/details/118667959

每个进程都会存有一个oom_score的参数,
比如输出pid为988的oom_score:
cat /proc/988/oom_score

OOM Killer 会在系统报OOM的时候,杀死当前score最高的进程,一般情况也就是占用内存最大的进程

我测试了我的进程OOM最大为12,OOM的取值范围好像是[-1000, 1000],感觉并不算大。

使用以下命令可以查看系统out of memory的发生时间

grep "Out of memory" /var/log/messages

使用以下命令可以查看系统杀掉过哪些进程

egrep -i -r 'killed process' /var/log

这里的时间跟我测试的时间差不多都能对应上,并且里面某一次的进程号我记得比较清楚(pid=28121),其他的不太记得了,但是基本可以确定程序确实被Linux杀掉了

而且经过多次测试观察,因为PDF页数特别多,每次PDF解析时占用CPU确实很大,但是占用内存只有1.5%左右,不知道为啥就被Linux给杀掉了。

首先系统内存应该没有不足,其次占用的内存相比较其他进程是大了一点,但是也没有非常大。

最后经过对PDF解析过程的优化,内存降低到了0.3%,针对问题数据连续跑了几个小时程序再也没被系统杀掉过。