Linux 报 Too many open files 怎么排查?进程限制、句柄泄漏与 systemd 修复

先区分 EMFILE 与 ENFILE,再按实际进程、服务启动方式和容量证据处理

排查 Linux Too many open files,区分进程 EMFILE 与系统 ENFILE,读取实际 PID 限制并修复泄漏或配置 systemd LimitNOFILE。

先分清是单个进程还是整台机器触顶:Linux 日志出现 Too many open files,最常见的是某个进程超过自己的 RLIMIT_NOFILE,对应 EMFILE;较少见的是全系统文件句柄达到 fs.file-max,对应 ENFILE。先从报错服务取得 PID,读取它的实际限制和文件描述符数量,再决定修复泄漏、调整 systemd 的 LimitNOFILE,还是处理全局容量。只执行 ulimit -n 65535 往往不会改变已经由 systemd 启动的服务。

读取状态通常不收费;新增监控、扩容或拆分服务可能产生费用。提高上限本身不会删除数据,但重启数据库、Web 服务或消息队列会中断连接,在途请求可能失败。若真正原因是描述符泄漏,盲目加大上限只会推迟故障,并让进程占用更多内核内存。操作前保存原限制、服务配置和当前连接状态。

从报错服务取得实际证据

先在宝塔的项目日志、网站日志或 SSH 中确认哪个服务报错,以及发生时间。Nginx、PHP-FPM、Java、Node、MySQL 和 Docker 都可能打开普通文件、网络 Socket、管道或事件对象;文件描述符不只代表磁盘文件。把下面的“你的服务名”和 PID 换成实际值,命令均为只读。

systemctl status 你的服务名 --no-pager
systemctl show 你的服务名 -p MainPID -p LimitNOFILE
PID=$(systemctl show 你的服务名 -p MainPID --value)
cat /proc/$PID/limits | grep -i 'open files'
find /proc/$PID/fd -maxdepth 1 -type l | wc -l
cat /proc/sys/fs/file-nr
sysctl fs.file-max

/proc/PID/limits 才是该运行进程的实际软、硬限制。systemctl show 显示的是 systemd 为服务设置的值,两者应结合看。/proc/sys/fs/file-nr 用于判断全系统已分配句柄是否接近最大值。若只有一个进程接近自身上限,而全局仍有大量余量,应先查该服务;若内核日志出现 VFS: file-max limit reached,才进入系统级分支。

EMFILE 和 ENFILE 应该走哪条处理路径

证据说明默认动作
单个 PID 的 FD 数接近软限制进程级 EMFILE识别描述符类型与增长来源,再评估服务级上限
全局 file-nr 接近 file-max系统级 ENFILE找出占用大户和异常增长,先止损再核算全局值
终端 ulimit -n 很高但服务仍报错交互 Shell 与 systemd 服务不是同一启动环境检查 unit 的 LimitNOFILE 与实际 PID
数量持续增长且业务量不变更像连接或文件未关闭修复应用泄漏,不能只提限制

紧急恢复时,如果服务已经无法接收连接,可以在维护窗口重启故障进程释放描述符,但这只是止损。数据库和队列重启前要确认主从、持久化、事务与客户端重连;Web 服务重启前要确认负载均衡或维护页。记录重启前的 FD 数和类型,否则故障证据会随进程退出消失。

怎样看进程到底打开了什么

如果系统安装了 lsof,先按类型汇总,不要把包含用户路径、IP、数据库文件名的完整清单公开。下面示例只读取目标 PID;结果较大时用 head 抽样。

sudo lsof -nP -p "$PID" | head -n 100
sudo lsof -nP -p "$PID" | awk 'NR>1 {print $5}' | sort | uniq -c | sort -nr | head
ls -l /proc/$PID/fd | head -n 100

大量 REG 可能是日志、缓存或数据文件没有关闭;大量 IPv4/IPv6 Socket 需要结合连接状态、上游超时和连接池判断;大量 pipeanon_inodeeventpoll 则要回到对应运行时与组件。仅凭“Socket 多”不能断定泄漏,高并发服务本来就需要描述符;关键是数量是否与业务量相称、请求结束后是否回落、是否持续单向增长。

systemd 服务应该怎样调整 LimitNOFILE

只有在确认业务正常峰值确实需要更多连接或文件,而且应用没有明显泄漏时,才提高服务级限制。先用 systemctl cat 你的服务名 查看供应商 unit 和现有覆盖文件,不直接编辑 /usr/lib/systemd/system 下的原文件。使用覆盖配置便于升级和回退。

[Service]
LimitNOFILE=65536

65536 只是配置格式示例,不是所有服务器的推荐值。应按工作进程数、最大连接数、日志/文件占用和预留量计算,同时确认应用自身也有连接上限。保存覆盖后先运行 systemd-analyze verify 检查 unit,再执行 systemctl daemon-reload;新限制只有在服务重启、产生新进程后才生效。重启前必须安排业务切换或维护窗口。

验证时重新读取新 PID 的 /proc/PID/limits。如果 systemctl show 已变化而进程仍是旧值,通常是服务尚未重启;如果应用自身还有 worker_connections、数据库连接池或运行时限制,还要单独核对,不能把所有限制都归给 Linux。

为什么只改 limits.conf 或 ulimit 不生效

/etc/security/limits.conf 主要通过 PAM 影响登录会话。由 systemd 直接启动的系统服务通常使用 unit 的 LimitNOFILE 或系统管理器默认值,不会继承你当前 SSH 终端临时执行的 ulimit。Docker 容器还可能同时受到宿主 Docker 服务、容器 ulimits 和容器内进程的限制;应从报错进程逐层读取,不要只在容器内或宿主机改一处。

宝塔管理的服务也可能由独立启动脚本或 systemd unit 拉起。先通过 ps -o pid,ppid,cmd -p PID 确认父进程与启动方式,再选择配置入口。不要为追求“立即生效”把限制写进多个文件,否则后续无法判断最终值来自哪里。

什么时候才需要调整 fs.file-max

只有全局 file-nr 长期接近 file-max,并且已经识别各服务的合理需求后,才评估系统级上限。多数单服务报错属于进程限制或应用泄漏,改 fs.file-max 没有帮助。若确需调整,应使用受控的 sysctl.d 配置文件,保留原值和容量计算,不用一次性 echo 留下无法追踪的生产变更。

系统级上限提高后还要确认内存、连接跟踪、端口、应用连接池和下游承载能力。能打开更多 Socket 不代表数据库或上游能处理更多请求。高负载与资源瓶颈可结合Linux 负载排查,Nginx 等待上游过久则参考Nginx 504 排查,容器健康失败可参考Docker Compose unhealthy 排查

修复后怎样验收和回退

  1. 用新 PID 复查实际软、硬限制和当前 FD 数。
  2. 在正常与峰值流量下连续观察 FD 数量,确认请求结束后能够回落。
  3. 检查应用错误日志和内核日志,不再出现 EMFILEENFILE 或 file-max 告警。
  4. 验证真实页面、后台任务、数据库连接和一次低风险写入,不能只看服务为 active。
  5. 记录合理峰值、告警阈值和增长速度,为下一次异常保留基线。

若提高限制后 FD 仍持续增长,应停止继续加码,回退 systemd 覆盖文件并恢复原服务配置,转向应用泄漏修复。删除覆盖配置后需要 daemon-reload 并在维护窗口重启,随后从新 PID 确认原值恢复。若新限制导致内存压力、连接洪峰或下游过载,应先摘流或降并发,再回退,避免直接杀死有状态进程。

上游资料

以上资料于 2026 年 9 月 4 日核验。不同发行版、systemd 版本、容器运行时和应用启动方式可能不同,最终以当前进程的 /proc/PID/limits 与上游文档为准。

云服务器教程Vue、React 单页应用刷新后 404 怎么办?Nginx History 路由与 API 分流2026-09-13云服务器教程Docker 提示 pull access denied 怎么办?镜像名、仓库权限与登录排查2026-09-13云服务器教程MySQL 报 ERROR 1205 Lock wait timeout 怎么办?阻塞事务与安全止损2026-09-12

加入开发者交流社区

与全球开发者、运维和工作室一起交流技术、分享经验、配置、账号与最新优惠信息

  • 云平台使用交流
  • 资源优惠信息
  • 最新教程与资讯
  • 开发者经验分享
联系 Telegram 客服
加入开发者交流社区