Too many open files是Linux系统中常见的错误,从字面意思上看就是说程序打开的文件数过多,不过这里的files不单是文件的意思,也包括打开的通讯链接(比如socket),正在监听的端口等等,所以有时候也可以叫做句柄(handle),这个错误通常也可以叫做句柄数超出系统限制。引起的原因就是进程在某个时刻打开了超过系统限制的文件数量以及通讯链接数。
错误原理
“too many open files”这个错误大家经常会遇到,因为这个是Linux系统中常见的错误,也是云服务器中经常会出现的,而网上的大部分文章都是简单修改一下打开文件数的限制,根本就没有彻底的解决问题。
本文就是帮助开发者理解这个问题的来龙去脉,确保大家读完本文之后能够彻底明白和解决这个问题。首先一定要明确机器为什么会报这个错误,其次才好更加深入的去解决这个问题,正如错误信息的字面意思,就是打开了过多文件,系统无法继续打开文件句柄了。
这里的file更准确的意思文件句柄(file handle),出现这个报错的大多数情况都是文件句柄(file handle)泄露,通俗的说就是文件句柄在不断的被打开,但是在使用完成之后却没有正常的关闭导致文件打开数不断的增加。
文件句柄泄露有多种原因,而不仅仅是打开文件,常见的来源有:套接字,管道,数据库连接,文件。正常情况下服务器本身是不会突然报这个错误的,一定是我们部署到云服务器上面的业务程序打开了太多文件没有关闭导致同时打开的文件数超出了系统的限制:
一种情况是程序本身就需要打开很多的文件句柄,这种情况就是打开的文件数大于系统本身的打开文件数限制,这时需要我们将系统的限制调高,后面会给出具体的方法;
另外一种情况就是我们的程序存在文件句柄使用完成之后没有正常的关闭的情况,通常是网络连接没关闭,文件打开没关闭等等,这时就需要我们修复程序中的bug,确保打开的文件最后都会关闭,网络连接也会关闭。
分析问题
为了准确的定位到具体问题在哪里,我们就需要使用lsof
命令,下面就lsof
相关信息做一个基本的介绍:
文件描述符:fd(file descriptor),在Linux系统中一切皆可以看成是文件,文件描述符是内核为了高效管理已被打开的文件所创建的索引,是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件 描述符。
linux命令lsof
(list system open files):列出系统打开的文件,在终端下输入lsof
即可显示系统打开的文件。lsof各个字段的含义:
COMMAND | PID | TID | USER | FD | TYPE | DEVICE | SIZE | NODE | NAME |
程序的名称 | 进程标识符 | 线程标识符 | 进程所有者 | 文件描述符 | 文件类型 | 设备编号 | 文件的大小(bytes) | 索引节点 | 打开文件的确切名称 |
FD 列中的常见内容有 cwd、rtd、txt、mem 和一些数字等等。其中 cwd 表示当前的工作目录;rtd 表示根目录;txt 表示程序的可执行文件;mem 表示内存映射文件。所以一般文件句柄打开的FD都是数字开头的,比如"0u","1u","2u"。
这里我们先使用lsof找出打开文件数较多的进程,然后再挨个排查其打开文件数的具体情况:
下面这个命令可以看到当前进程文件打开数的数量排序,第一列是打开文件数,第二列是pid,由于lsof
的结果会包含线程和系统默认类型的FD,和实际的FD打开数区别较大,所以还需要根据这个命令的排序拿对应的pid再去排查真实的FD打开数量:
上面的结果虽然和实际有区别,但是排序基本上是比较一致的,然后再使用下面这个命令查看对应pid进程真实打开的fd数量,看看是否过高,通常来说超过1000就算过高了,如果定位到了具体的进程,然后就要检查对应的程序了。
可以看到实际进程pid 17336
的打开FD数为47,那1652个计数是因为有很多线程都共用了这47个打开的文件,所以最后计算的结果与实际的结果区别较大。我们还可以根据 ls /proc/<pid>/fd 这个命令来确认一下最终的结果:
可以看到进程17336
目录下就是只打开了47个fd,属于正常的打开文件数量。另外,硬盘文件删除后没有释放磁盘空间也是这个原因,因为删除文件的文件句柄未关闭,也可以使用上面的方法去排查。