
网站突然无法建立新连接,应用日志里反复出现 Too many open files,但磁盘空间和内存看起来都够用。这时需要检查文件描述符。Linux 中的普通文件、网络套接字、管道等都可能占用描述符;连接没有释放,也会让服务走到限制边界。
处理这种故障,先确定报错进程及其实际限制,再判断资源增长是否符合业务负载。只修改登录终端里的 ulimit,通常影响不到已经运行的 systemd 服务;把上限调大,也可能仅仅延后下一次故障。
本文命令适用于常见 Linux 主机,以 systemd 管理的服务为例。服务名、PID 和路径均需替换为现场值。容器还需检查容器内进程和运行时配置。本文没有连接生产服务器执行测试。
报错发生在哪个进程,决定检查范围
应用日志中的 Too many open files 通常对应进程达到文件描述符限制,即 EMFILE。系统范围文件句柄耗尽对应 ENFILE,错误文字可能包含 Too many open files in system。包装库可能改写错误消息,不能只凭网页上的一行提示定性。[1]
先从服务日志中找到报错时间、进程和失败操作。数据库、反向代理和业务应用都有独立限制,前端返回 502 并不代表 Nginx 自己耗尽了描述符。
下面是只读检查示例,将 example.service 替换为实际单元名。
systemctl status example.service --no-pager
journalctl -u example.service --since '30 minutes ago' --no-pager
systemctl show example.service -p MainPID -p LimitNOFILE -p LimitNOFILESoft
MainPID 只能作为入口。采用主进程加 worker 模型的应用,故障可能发生在某个 worker;需要结合进程树及日志检查子进程。MainPID 为 0 时,先确认服务是否已经退出,不能拿 0 当作待检查的业务 PID。
读取运行中进程的限制和描述符数量
以下示例使用 PID 1234,执行前务必换成已核实的 PID。读取其他用户的进程信息可能需要相应权限。
cat /proc/1234/limits
ls -1U /proc/1234/fd | wc -l
ls -l /proc/1234/fd | head -n 40
在 limits 中找到 Max open files,分别查看软限制与硬限制。软限制是当前生效边界;硬限制约束进程提高软限制的能力,特权进程还受相关内核规则约束。[1]
描述符目录中的条目数可用于观察占用,但计数发生时进程仍在运行,因此只是瞬时快照。权限不足、进程退出或 PID 被复用都可能使结果失真。不要把错误输出当作“占用为零”。
还需注意,RLIMIT_NOFILE 定义的是可分配描述符编号的上界加一,条目总数适合日常趋势判断,却不等同于所有情况下的严格剩余额度。[1]
如果系统安装了 lsof,可以进一步查看类型和目标。
sudo lsof -nP -p 1234
sudo ss -tanp
lsof 输出行数不能直接等同于描述符数量,其中可能包含工作目录、程序映像和内存映射等记录。ss 用于检查 TCP 状态和关联进程,也不能覆盖普通文件、Unix 套接字或全部描述符类型。两者应与 /proc/PID/fd 交叉判断。
区分正常连接高峰与持续泄漏
单次高占用无法证明泄漏。记录同一个进程在高峰前、高峰中和流量回落后的数量,并保留进程启动时间,避免跨重启比较失去意义。
通常可以从目标类型缩小范围:
- 套接字随并发上升,流量下降后逐渐回落,可能是正常容量需求,需要结合连接池、长连接和超时配置评估。
- 流量已回落,占用仍逐次攀升,需检查响应体、数据库连接、文件对象和异常分支是否正确关闭。
- 大量
CLOSE-WAIT连接值得检查本地应用关闭连接的逻辑,但仅凭该状态还不能确认具体代码缺陷。 - 同一批文件反复打开,可能与日志处理、文件监控、插件行为或业务任务有关,应追溯实际持有者。
不要用 TIME-WAIT 数量推导某个进程的打开文件数。这类 TCP 状态通常已经不再由原应用持有文件描述符。
全量收集 lsof、连接表或高频扫描进程目录,在连接规模很大时也有成本。线上可先短时采样,再对可疑进程细查。输出中可能含内部路径、远端地址等信息,分享日志前需要脱敏。
systemd 服务的上限需要在正确位置修改
当前终端运行 ulimit -n,看到的是这个 shell 的软限制。它不能证明后台服务采用了同一数值。登录会话的 PAM 限制与 systemd 系统服务的启动路径也不能混为一谈。
对于 systemd 服务,检查单元配置和 drop-in,明确 LimitNOFILE 的来源。
systemctl cat example.service
systemctl show example.service -p LimitNOFILE -p LimitNOFILESoft
如确认需要调整,应先记录旧值、保存现有 drop-in,并安排维护或滚动重启。下面只展示语法,32768 是示例值,不能直接作为所有应用的推荐配置。
[Service]
LimitNOFILE=32768:32768
可通过 systemctl edit example.service 添加覆盖配置。冒号前后分别为软限制和硬限制。systemd 文档明确提醒,提高软限制时要留意旧程序对 select(2) 的依赖;这类程序可能无法处理大于 1023 的描述符编号。应用是否采用兼容机制,需要查应用文档和运行条件。[2]
修改后重新加载单元配置,并在批准的窗口重启服务。
sudo systemctl daemon-reload
sudo systemctl restart example.service
daemon-reload 不会改变已运行进程的限制。普通业务配置 reload 也不能保证更新资源限制。重启可能中断请求、后台任务或长连接,需要提前排空流量并准备回退。
服务重新启动后,从新 PID 的 /proc/PID/limits 核实生效值,再检查 worker;仅看到配置文件里写了新数字还不够。有些程序启动后会自行调整限制,实际进程状态仍需单独确认。
容器场景要检查 Compose 或运行时传入的 nofile 限制,以及容器内实际进程的限制。修改宿主机某个登录会话的 ulimit 不会自动改好现有容器,配置变更往往需要按平台流程重建或重启工作负载。
系统级句柄耗尽有不同的证据
如果多个互不相关的服务同时无法打开文件,或者日志指向 ENFILE,再检查系统范围状态。
cat /proc/sys/fs/file-nr
cat /proc/sys/fs/file-max
cat /proc/sys/fs/nr_open
file-nr 给出已分配文件句柄数、已分配但未使用数及最大值。现代 Linux 的第二项通常为 0,解释时需要考虑内核版本。系统文件句柄数量与各进程描述符数量并非简单的一对一总和。[3]
file-max 控制系统范围可分配的文件句柄数量;nr_open 与单进程可设置的描述符限制上界有关。提高其中一个参数,不能替代对另一个层级的检查。[1][3]
系统级调参会影响整机。没有确认耗尽范围、内存预算和应用行为时,不应直接复制一组很大的 sysctl 数字。临时恢复要同步记录原值、有效期和撤销条件,后续仍需处理异常增长来源。
恢复后要验证能否稳定承接原有业务
重启可释放进程持有的描述符,却会抹掉一部分故障现场。情况允许时,重启前保留限制、类型分布、连接状态和日志;已经影响业务时则按应急预案决定取证与恢复的优先级。
验收至少覆盖新建连接、文件读写、数据库访问、后台任务和原先失败的接口。持续观察描述符占用能否随负载回落,确认错误日志不再增长,并核对延迟和失败率,避免“网站首页打开了”掩盖后台任务仍失败。
如果提高限制后占用继续单向增长,就需要回到连接生命周期或文件释放逻辑。新增容量可以缓解故障,但不能作为泄漏修复的验收结果。回退时恢复本次修改的 drop-in 和原有限制,再按同样流程加载、重启与复核;不要为回退一个参数而删除其他已有覆盖配置。
长期监控可以同时记录进程描述符数量、生效软限制、进程启动时间和业务并发。告警阈值应结合基线与增长速度设置。持续上升且流量已经回落的趋势,比一次孤立峰值更值得追查。
参考资料
资料核验日期:2026 年 10 月 28 日。文中命令是排查示例,调整前需核对发行版、systemd 版本及应用文档。
[1] Linux man-pages:getrlimit(2),RLIMIT_NOFILE、EMFILE 与限制规则。https://man7.org/linux/man-pages/man2/getrlimit.2.html
[2] systemd.exec:LimitNOFILE、软硬限制及 select 兼容性说明。https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
[3] Linux man-pages:proc_sys_fs(5),file-nr、file-max 与 nr_open。https://man7.org/linux/man-pages/man5/proc_sys_fs.5.html




