一、问题概述

部分服务器在长时间运行后出现了/tmp空间告警(大约半年)

ios finder查看真机tmp目录下的文件_服务器


磁盘空间占用过高时因为我们部署的进程占用了/tmp下已删除的文件,导致文件并没有真正释放,并且进程在持续写文件。这实际上是一种句柄泄漏。可以通过命令查看:

lsof |grep -i del|grep tmp

ios finder查看真机tmp目录下的文件_服务器_02


可以看到被占用的文件以及占用进程。可以确认这种文件不是进程中主动打开的文件,而是linux在某种机制下创建的临时文件。解决这个问题,最直接的办法是重启所有占用了此文件的进程,如上图的进程59613,但这种做法代价太大。根本的解决方案是优化代码,找到可能泄漏资源的地方。

二、临时解决方案

临时有个相对优雅的解决方案,可以清掉文件内容,解决空间告警。进程所打开的所有文件,在/proc/PID/fd/下均有记录(即文件描述符),linux的机制是允许删除被进程占用的文件,但只有所有进程解除占用后才会真正释放空间(这一点与windows不同,windows不允许删除被占用的文件)。因为/tmp下文件已经被删除了,所以无法按文件路径和文件名清空,但是因为进程下打开的文件描述符还在,所以可以通过清文件描述符的方式清空这个文件。
echo > /proc/PID/fd/FD 或者
truncate -s 0 /proc/PID/fd/FD 其中PID和FD是变量,需要根据实际情况填写。方法如下:

命令lsof展示的各列内容为:

ios finder查看真机tmp目录下的文件_服务器_03


ios finder查看真机tmp目录下的文件_服务器_04

三、根因定位

即然我们能根据文件描述符清空该文件,我们也能查看该文件的内容:
tail -f /proc/PID/fd/FD 然后发现,文件内容全部是上述进程59613打印的日志!查看代码,日志打印全部采用的标准syslog打印,不可能出现这种泄漏。然后通过写dome测试、逐步缩小范围等等措施,最后发现是自动启动脚本导致的问题。内容如下:
nohup ./program -param & 我们使用nohup(no hang up)命令将进程扔到后台执行。如果我们没有对进程的打印信息进行重定向,nohup指令则会在进程路径下创建一个nohup.out文件用于输出进程的打印信息。

同时,我们为了让进程具备故障自动恢复的能力,将这个启动脚本交给了corntab定时执行。实测发现,当脚本手动执行时,会在进程路径下创建nohup.out文件,当脚本由crontab执行时不会创建这个文件,而问题就出现在这里!当没有nohup.out文件时,linux操作系统会将这部分打印在tmp空间创建一个临时文件,然后输出,而tmp下的文件会被tmpwatch定时删除。但是因为进程还在不停打印日志,这个临时文件尽管已经被删除但是句柄并没有被进程释放,最终就会导致tmp空间泄漏!

所以最终的解决方案是对进程日志进行重定向
nohup ./program -param >/dev/null 2>&1 & 注:即是将日志扔进黑洞设备,同时标准错误输出重定向到标准输出。日志扔进黑洞设备是没有关系的,进程中使用syslog标准打印还是会打印到syslog中。