这条路径解决什么
服务报 Too many open files、磁盘明明有空间却写入失败、文件删了空间不释放,这些问题的共同点是「进程持有的句柄」。lsof 按进程或文件列出当前打开的文件描述符,配合 /proc/PID/limits 与 /proc/PID/fd 就能判断是上限设小了,还是程序自己在泄漏句柄。
基本信息
| 适用场景 | 句柄不足、句柄泄漏、删除文件空间不释放 |
|---|---|
| 核心工具 | lsof 列表、/proc/PID/limits 看上限、/proc/PID/fd 数句柄 |
| 常用组合 | lsof -p PID、lsof +L1 找已删除但仍被持有的文件 |
| 判据 | 句柄数接近上限即为瓶颈;数量随时间持续增长即为泄漏 |
| 权限说明 | 查看其它用户的进程需要 root |
典型使用场景
- 报 Too many open files:确认当前句柄数与实际上限,找出是谁在开文件
- 句柄泄漏排查:按时间多次采样数量,判断是否持续增长
- 空间不释放:文件已删但进程仍持有,用 lsof +L1 定位
- 配置核对:确认服务进程真正生效的 nofile 上限
- 发布前排雷:确认新版本没有打开大量连接或文件
常用示例
常用示例
lsof -p 1234 | wc -l # 该进程当前句柄数
lsof -p 1234 | awk '{print $5}' | sort | uniq -c | sort -rn | head # 按类型统计
cat /proc/1234/limits | grep -i 'open files' # 实际上限(软限与硬限)
ls /proc/1234/fd | wc -l # 更快的句柄计数
lsof +L1 2>/dev/null | grep -i app # 已删除但仍被打开的日志
lsof -u www-data | wc -l # 按用户统计总量观察趋势
观察趋势
while true; do
printf '%s %s\n' "$(date +%T)" "$(ls /proc/1234/fd | wc -l)"
sleep 60
done句柄上限的三种来源
| 来源 | 作用范围 | 查看方式 |
|---|---|---|
| ulimit -n | 当前 shell 及其子进程 | 在对应会话里执行 ulimit -Hn 与 -Sn |
| /etc/security/limits.conf | 经 PAM 登录的会话 | grep 用户名 与 nofile 行 |
| 单元里的 LimitNOFILE | 该 systemd 服务 | systemctl show 服务名 -p LimitNOFILE |
| systemd 全局 DefaultLimitNOFILE | 未被单独指定的服务 | grep 系统级 systemd 配置 |
| 容器运行时限额 | 容器内进程 | docker inspect 或编排文件里的 ulimit 配置 |
Too many open files 要三处一起查:会话侧的 ulimit -n、登录策略的 limits.conf、服务单元里的 LimitNOFILE。systemd 服务只看单元或全局默认值,完全不读 limits.conf,这是「配置了却不生效」的典型原因。另外 lsof 的结果受权限限制:看不到其它用户进程的句柄时,用 sudo 重跑再统计,否则数量会被低估。
排查句柄泄漏的关键是趋势而不是瞬时值:定时采样 /proc/PID/fd 的数量并记录,只有在业务量稳定时数量仍持续上升,才能认定为泄漏。瞬时的峰值可能只是并发连接波动,不必急于改上限。另外句柄需求与并发模型强相关:Nginx、Java 这类进程常常一个连接占一个描述符,评估上限要按峰值连接数留出余量。