Linux 文件句柄耗尽表现为“Too many open files”,根源常是限制配置不当而非资源真用尽;需从使用量、限制阈值、应用行为三方面排查,合理设置 ulimit 与 file-max,并识别修复 fd 泄漏。

Linux 文件句柄(file descriptor,简称 fd)耗尽会导致进程无法打开新文件、建立网络连接或写日志,典型表现是报错 Too many open files。问题根源常在于系统级或进程级限制未合理配置,而非真的用光了内核资源。排查和优化需从当前使用量、限制阈值、应用行为三方面入手。
查清当前 fd 使用情况
先确认是否真“耗尽”,而不是某进程卡死或泄漏:
- 查看系统级已分配 fd 总数:
cat /proc/sys/fs/file-nr,输出三列:已分配但未使用的 fd 数、已使用的 fd 数、系统最大 fd 数(fs.file-max) - 查看某进程打开的 fd 数量:
ls -l /proc/,或用/fd/ | wc -l lsof -p| wc -l - 快速统计各进程 fd 占用排名:
lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | head -10
理解 ulimit 限制层级
Linux 中 fd 限制分三层,优先级由高到低:进程启动时继承的ulimit -n → 用户级/etc/security/limits.conf → 系统级/proc/sys/fs/file-max。常见误区是只调大file-max,却忽略用户级限制导致应用仍受限。
- 临时修改当前 shell 会话限制:
ulimit -n 65536(仅对后续启动的子进程生效) - 永久设置用户级限制:在
/etc/security/limits.conf中添加(注意替换username或用*):
username soft nofile 65536
username hard nofile 65536 - systemd 服务需额外配置:
/etc/systemd/system.conf或服务单元文件中加LimitNOFILE=65536,然后执行systemctl daemon-reload
识别并修复 fd 泄漏
即使限制调高,若程序存在 fd 泄漏(如打开文件 /Socket 后未 close),迟早再次耗尽。关键看 fd 类型分布:
- 用
lsof -p查看具体打开哪些 fd:大量REG(普通文件)、IPv4/IPv6(socket)、anon_inode(eventfd、timerfd 等)都可能指向不同问题 - 常见泄漏场景:日志框架反复 open 文件未复用 FileHandler;HTTP 客户端未关闭 response body;数据库连接池未正确归还连接;异步 IO 未清理完成事件
- Java 应用可配合
jstack+lsof交叉分析线程堆 栈与 fd 归属;Go 程序用pprof的/debug/pprof/goroutine?debug=2辅助定位阻塞点
合理设置 fd 上限值
盲目设为 unlimited 或百万级反而有风险:内存开销上升、内核查找 fd 变慢、掩盖真实泄漏。推荐按场景分级设置:
- 普通 Web 服务(Nginx/Redis):soft/hard 均设为
65536,覆盖大多数并发连接需求 - 高并发网关或消息中间件:可设为
262144(256K),但需同步调整net.core.somaxconn和net.ipv4.ip_local_port_range - 开发测试机可适当降低(如
8192),便于早期暴露泄漏问题 - 始终保证
fs.file-max ≥ 所有进程 hard nofile 之和 × 1.2,留出内核自身开销余量