Linux文件句柄耗尽本质是进程FD数超限导致系统调用失败,主因是FD泄漏长期积累;需先验证报错、检查进程FD数与limit、全局file-nr,再用lsof等定位大户及FD类型,最后排查代码未关闭资源、连接池失控、子进程继承及系统限制过低等问题。

Linux文件句柄耗尽,本质是进程打开的文件描述符(FD)数量超过系统限制,导致新 open、socket、pipe 等系统调用失败(常见报错:Too many open files)。这通常不是瞬时峰值,而是 FD 泄漏(FD leak)长期积累所致——即程序申请了 FD 却未正确 close,导致其持续占用不释放。
确认是否真为 FD 耗尽
别急着查代码,先验证现象:
- 查看报错进程的 error log,确认是否含 "Too many open files" 或 errno=24
- 查该进程当前打开的 FD 数量:ls -l /proc/<PID>/fd/ | wc -l
- 对比其 soft limit:cat /proc/<PID>/limits | grep "Max open files"(注意 soft 和 hard 区别)
- 检查全局可用 FD 余量:cat /proc/sys/fs/file-nr(三列分别为:已分配、已使用、最大上限)
定位泄漏源头进程
若确认是 FD 耗尽,需快速锁定“大户”:
- 按打开 FD 数排序所有进程:lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | head -20
- 或更精准(排除 lsof 自身干扰):for pid in /proc/[0-9]*; do p=$(basename $pid); c=$(ls -1 $pid/fd 2>/dev/null | wc -l); [ $c -gt 100 ] && echo "$p $c"; done | sort -k2 -nr | head -10
- 重点关注:长期运行、频繁建连接(如 Web server、DB client、消息队列消费者)、或存在子进程反复 fork 的服务
分析具体 FD 类型与归属
对嫌疑 PID,深入看它到底打开了什么:
- 列出所有 FD 及类型:lsof -p <PID>(加 -n 不解析域名,更快)
- 统计 FD 类型分布:lsof -p <PID> | awk '{print $5}' | sort | uniq -c | sort -nr(关注 REG 文件、IPv4/6 socket、PIPE、anon_inode 等)
- 检查异常项:大量处于 ESTABLISHED 但无业务逻辑应保持的连接;大量 deleted 状态文件(曾被 unlink 但未 close);重复出现的同一路径或 IP:PORT
代码与配置层面的关键排查点
FD 泄漏多源于编程疏漏或配置不当:
- 未关闭资源:Go 中 defer close() 写在错误分支外;Java 中 FileInputStream 忘记 try-with-resources;Python 中 with open() 缺失或异常跳过 close()
- 连接池失控:DB 连接池 maxIdle/maxOpen 设置过大且未及时 evict;HTTP client 复用 connection 但未设置 timeout 或复用策略失效
- 子进程继承 FD:父进程打开的 FD 默认被 fork 出的子进程继承(尤其 socket、log file),需在子进程中显式 close() 或设 FD_CLOEXEC
- 系统级限制过低:单进程 limit -n 值太小(ulimit -n),或内核 fs.file-max 不足,可通过 sysctl -w fs.file-max=... 临时调整










