Linux服务器上的网站、接口或后台程序运行一段时间后,可能突然出现以下报错:
Too many open files
随后,程序可能无法打开文件、建立连接或接受新的请求。重启服务后暂时恢复,但运行一段时间又出现相同问题。
这类故障需要同时检查进程实际使用的文件描述符、资源限制和占用增长情况。本文以Debian、Ubuntu服务器为例,介绍一套可以逐步执行的排查方法。
1. Too many open files是什么意思?
Linux程序打开普通文件、网络套接字或管道时,通常会使用文件描述符,简称FD。
因此,“打开文件过多”不一定是程序打开了大量磁盘文件,也可能是网络连接、管道或其他对象占用了描述符。
在普通文件打开等场景中,可以先区分两类错误:
| 错误标识 | 主要含义 | 优先检查 |
|---|---|---|
EMFILE | 触及单个进程的文件描述符限制 | 出错进程的限制与占用 |
ENFILE | 触及系统范围的打开文件上限 | 系统文件句柄总量与上限 |
这两种情况的处理位置不同。只提高系统的fs.file-max,不能直接解决某个进程自身限制过低的问题。
如果日志只有一句英文提示,还应结合具体失败操作和程序上下文判断,不能只凭文字就修改全部系统参数。
2. 操作环境与准备
本文主要适用于使用systemd管理系统服务的Debian、Ubuntu服务器。
所有命令均在服务器终端执行。使用具有sudo权限的账户操作;如果已经使用root登录,可以去掉sudo。
以下内容中:
myapp.service是示例服务名,需要替换为实际服务。2468是示例PID,需要替换为出错进程的实际编号。- 配置中的限制数值用于演示,需要结合应用要求和现有配置确定。
本教程可在莱卡云Linux云服务器上操作,已有服务器的用户可以直接检查当前环境。
查看官网购买链接: https://www.lcayun.com
9折优惠码:bloglcayun
3. 先确认是哪个进程报错
如果程序由systemd管理,先查看服务状态:
sudo systemctl status myapp.service --no-pager -l
查看主进程PID:
sudo systemctl show myapp.service -p MainPID
查看本次开机期间,该服务最近的日志:
sudo journalctl -u myapp.service -b -n 100 --no-pager
重点记录报错时间、进程编号,以及错误发生在打开文件、接受连接还是其他操作中。
如果MainPID=0,说明当前没有可供查询的主进程,应先检查服务状态,不能继续使用PID 0进行后续排查。
多进程服务还需要检查实际报错的工作进程。 主进程的占用较低,不代表所有工作进程都正常。
找到目标PID后,可以核对进程身份:
ps -p 2468 -o pid,ppid,user,comm
如果程序已经自动重启,PID可能发生变化,应重新确认。
4. 检查进程的实际限制与占用
4.1 查看文件描述符限制
执行:
sudo cat /proc/2468/limits
在输出中找到:
Max open files
下面仅用于解释字段,不代表实际服务器输出:
Limit Soft Limit Hard Limit Units
Max open files 1024 524288 files
其中:
- Soft Limit: 当前执行的软限制。
- Hard Limit: 软限制可以调整到的上限,普通进程不能自行突破它。
/proc/PID/limits反映目标进程当时的资源限制,应以这里的实际结果为依据。
4.2 查看当前占用了多少描述符
执行:
sudo ls -1U /proc/2468/fd | wc -l
该命令统计目标进程FD目录中的条目数量。
如果占用接近软限制,并且同一时间出现EMFILE错误,就应重点检查该进程的资源使用。
注意两点:
- 进程不断打开和关闭文件,统计结果只是当时的快照。
- 如果出现权限不足或目录不存在等错误,统计结果不能作为有效依据。
一次查询数量正常,也不能排除此前发生过短时峰值。
4.3 为什么ulimit显示很大,服务仍然报错?
在当前Bash终端执行:
ulimit -Sn
ulimit -Hn
这两条命令分别查看当前Shell的软限制和硬限制。
Shell的限制会影响它后续启动的进程,但不代表已经运行的systemd服务也采用相同数值。
排查服务时,应以出错PID的实际限制和占用为依据。

5. 找出描述符主要被什么占用
查看目标进程FD对应的对象:
sudo ls -l /proc/2468/fd
常见结果可以这样理解:
| 链接目标或形式 | 对应对象 |
|---|---|
| 普通文件路径 | 日志、配置、数据文件等 |
socket:[编号] | 网络套接字或Unix套接字 |
pipe:[编号] | 管道 |
anon_inode:[eventpoll]等 | epoll等内核对象 |
这些对象本身都可能正常存在,不能看到大量socket或某个特殊名称,就直接认定程序异常。
接下来,应在相近业务负载下观察占用变化:
- 请求结束后,连接和描述符是否能够回落?
- 是否持续打开相同类型的文件,却没有及时关闭?
- 客户端连接池是否过大,或者反复创建新连接?
- 异常分支、超时处理是否遗漏了资源释放?
如果占用随业务增长后保持稳定,可能需要结合正常峰值调整容量;如果长期持续上涨,则应进一步检查资源泄漏或回收逻辑。
如果链接目标出现(deleted),并且同时存在删除文件后磁盘空间没有释放的问题,可以参考本站文章:
6. systemd服务如何调整文件描述符限制?
确认服务确实需要更高上限后,再修改对应服务的配置。
调整前应确认应用支持较高的文件描述符编号。 使用传统select()接口的程序,在Linux上处理超过1023的描述符可能存在兼容性问题,不能把调大限制作为所有程序的通用方案。
6.1 编辑服务覆盖配置
下面沿用第4节的示例:原软限制为1024、硬限制为524288,假设应用要求将软限制提高到65536,并保留原硬限制。
执行:
sudo systemctl edit myapp.service
在编辑器的可编辑区域加入:
[Service]
LimitNOFILE=65536:524288
如果已经存在[Service]段,应修改对应设置,并保留其他配置。
冒号前是软限制,后面是硬限制。示例数值不是所有服务器的推荐值,应根据实际查询结果和应用要求调整。
6.2 加载配置并重启对应服务
保存退出后,在允许短暂中断业务的维护时间执行:
sudo systemctl daemon-reload &&
sudo systemctl restart myapp.service
然后检查状态:
sudo systemctl status myapp.service --no-pager -l
daemon-reload用于重新加载服务定义;重启服务后,新启动的进程才会按照相应配置运行。仅修改文件或执行daemon-reload,不会改变旧进程的资源限制。
如果服务修改后无法启动,可以结合本站文章继续排查:
6.3 检查新进程是否采用了配置
查看服务管理器中的相关属性:
sudo systemctl show myapp.service \
-p MainPID \
-p LimitNOFILESoft \
-p LimitNOFILE
其中,LimitNOFILESoft对应软限制,LimitNOFILE对应硬限制。
再读取当前主进程的实际限制:
app_pid=$(sudo systemctl show myapp.service -p MainPID --value)
if [ "$app_pid" -gt 0 ] 2>/dev/null; then
sudo cat "/proc/$app_pid/limits"
else
printf '%s\n' '未找到运行中的主进程,请检查服务状态。'
fi
多进程服务还应重新确认工作进程PID,并按第4节的方法检查。部分应用会在启动过程中自行调整限制,因此只看服务管理器属性还不够。
最终应以实际运行进程的limits为准,并结合业务请求确认恢复情况。

7. 如果是系统文件句柄总量达到上限呢?
如果错误信息指向ENFILE,或者多个无关服务同时出现文件打开失败,可以检查系统范围的使用情况:
cat /proc/sys/fs/file-nr
这三个数字依次表示:
- 已分配的文件句柄数量。
- 已分配但未使用的数量。
- 系统允许的最大数量。
现代Linux中,第二个数字通常为0,这本身不代表异常。
继续查看:
sysctl fs.file-max fs.nr_open
两个参数的含义不同:
| 参数 | 作用 |
|---|---|
fs.file-max | 系统范围的文件句柄分配上限 |
fs.nr_open | 单进程文件描述符硬限制可设置的内核上界 |
系统文件句柄统计与各进程FD数量并非简单的一一对应关系,不应直接把所有进程的FD数量相加后与file-max比较。
只有确认系统总量确实触及上限后,才应结合内存、业务规模和异常占用评估是否调整。当前查询值已经回落时,还需要结合故障时段的记录判断。
8. 如何确认问题真正解决?
修改完成后,建议检查以下三项:
- 限制是否生效: 新进程的
Max open files符合预期。 - 业务是否恢复: 原先失败的文件操作、连接或请求能够正常完成。
- 占用是否稳定: 在相近负载下观察FD数量,确认没有持续异常增长。
同时检查修改后的服务日志:
sudo journalctl -u myapp.service \
--since "10 minutes ago" \
--no-pager
如果提高上限后,程序只是更晚出现同样的错误,说明还需要继续处理文件或连接的增长来源。
对于手动从Shell启动的程序、容器中的程序以及其他进程管理器启动的服务,应检查各自的启动配置。本文的systemd设置不能直接覆盖所有启动方式。
9. 写在最后
遇到Too many open files,先找到实际报错的进程,再核对它的限制、占用对象和增长趋势。
如果属于合理业务量超过现有限制,可以按照应用要求调整;如果存在资源未释放的问题,则需要修复连接或文件的管理逻辑。
把故障时间、实际PID、限制数值和处理结果记录下来,后续再次出现类似问题时,就能更快判断是容量变化还是程序异常。








