排查file-max limit 65536 reached错误

近期遇到服务器宕机,重启后排查系统日志,看到/var/log/messages中在宕机前出现大量file-max limit 65536 reached错误:

报file-max limit reached,说明机器上文件句柄的使用量超过了设定值,以下是排查过程的整理。先说怎么看文件句柄数,这个数据记在/proc/sys/fs/file-nr中:

图中1056表示当前已打开文件句柄数量,65536表示最大数量。最大值由/proc/sys/fs/file-max控制,可以通过echo 100000 > /proc/sys/fs/file-max来修改。由于出现宕机前新部署了应用,因此怀疑与它有关,通过不断刷新,发现此应用运行时确实导致文件句柄上升,一段时间后上升至6000多。在linux下很多操作都会占用文件句柄,比如打开文件、打开目录、socket、pipe、共享内存等,首先怀疑有文件open后没有close,用lsof查下:

可以看到数量很少,排查系统内其它进程,lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | head,看到没有哪个进程占用大量文件,那多出的5000多个句柄应该不是文件,往共享内存方向排查,用pmap看看进程是否有mmap相关操作:

5392,这下对上了,用pmap进一步分析,发现有大量config.dat存在:

找到开发排查源码,发现对此文件的使用涉及了mmap操作,先open文件,然后用mmap把文件映射到内存,再close文件,结果用完后没有munmap操作,导致文件句柄不断上涨。

问题定位了,翻回来看为什么文件close了依然会导致文件句柄上涨。每个打开的文件都会有一个句柄用于描述文件属性,进程中用一个整数型的文件描述符指向具体的句柄。由于linux内核中对文件描述符是使用引用计数管理,mmap后,引用计数+1,此时尽管close了文件描述符,但引用计数不为0,内核不会回收文件句柄。

写个小程序验证一下:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>

int main()
{
    int i;
    for(i = 0;i < 1000;++i)
    {
        int fd;
        char * addr;
        fd = open("/dev/zero", O_RDONLY);        //打开文件
        if(!fd)
            exit(1);

        addr = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);        //映射
        //munmap(addr, 4096);                    //此行不执行就会导致句柄上涨
        close(fd);                                //关闭文件
    }

    pause();
}

 

重复1000次,每次open和close是匹配的,但mmap没有munmap,执行后的效果:

和预期一致,加上munmap后,对比测试则不会再出现这1000个句柄占用。

 

posted on 2021-07-14 15:38  BoyTNT  阅读(530)  评论(0编辑  收藏  举报

导航