查指定进程打开的文件应使用lsof -n -P -p PID,加sudo确保权限,避免DNS/端口解析卡顿;真实句柄数须用ls -1 /proc/PID/fd/ 2>/dev/null | wc -l验证。

直接用 lsof -p PID 就能列出该进程当前所有打开的文件句柄,但要注意它不是“计数器”,也不是万能快照——输出是否完整、是否含隐藏 fd、是否受权限/超时影响,得看你怎么用。
查指定进程的所有打开句柄:用 lsof -p PID,但别只信第一眼结果
这是最常用也最容易出错的操作。比如你想看 PID 1234 打开了什么:
- 必须加
sudo(除非是自己启动的进程),否则会漏掉 root 进程或跨用户资源; -
-n和-P建议带上:sudo lsof -n -P -p 1234,避免 DNS 反解卡顿、端口被转成服务名干扰判断; - 输出里
FD列出现eventpoll、inotify、timerfd等类型,说明进程用了 epoll 或异步 I/O,这类 fd 在高并发服务(如 Node.js、Nginx)中占比可能超 80%,但lsof默认可能不显示全——手册没写,但实测在 fd 数过万时容易截断或跳过; - 别用
lsof -p 1234 | wc -l统计总数,表头占一行、解析失败时可能中断、还跳过内核级 fd,结果不准。
验证句柄真实数量:直接数 /proc/PID/fd/ 目录
这才是 Linux 内核暴露给用户的“真相接口”,比 lsof 更底层、更稳定:
- 执行
ls -1 /proc/1234/fd/ 2>/dev/null | wc -l,结果就是当前真实打开的 fd 数量; -
2>/dev/null必须加,否则遇到权限不足的 fd(比如其他用户创建的 socket)会混入错误行,导致计数偏大; - 这个值不含已 close 但尚未被内核回收的残留 fd(极少见),也不含
lsof可能漏掉的 eventfd/timerfd,所以它代表的是“此刻内核认可的活跃句柄数”; - 普通用户只能读自己进程的
/proc/PID/fd/,root 才能读全部。
定位具体被占用的文件或端口:从路径或端口反查进程
当你知道一个文件删不掉、端口起不来,但不知道谁在用,就得反向查:
- 查文件被谁占:
lsof /var/log/app.log,注意路径必须是绝对路径,且不能带通配符; - 查目录及子目录:
lsof +D /tmp,但小心——它会递归扫描,权限不足时可能卡住或报错,生产环境慎用; - 查端口:
sudo lsof -i :8080 -n -P,NAME列显示*:8080 (LISTEN)表示监听,192.168.1.100:8080->10.0.0.5:54321 (ESTABLISHED)表示连接; - 查已删除但仍占空间的文件:
lsof +L1,输出中NAME列带(deleted)字样的就是,对应FD通常是数字+r/w,不是DEL(那是旧版字段,新版本已弃用)。
为什么 lsof -p PID 有时看起来“少了”或“不一致”
这不是 bug,是设计使然——lsof 的目标是“调试可见性”,不是“精确审计”:
- 它依赖
/proc/PID/fd/遍历,但某些内核 fd(如epoll实例本身)在/proc/PID/fd/下是符号链接,而lsof解析时可能因权限或路径长度失败,直接跳过; - Java 应用如果用了
sun.misc.Unsafe或 JNI 直接操作 fd,lsof可能完全看不到那些 fd; - Docker 容器里跑的进程,若挂载了
procfs以外的伪文件系统(如nsfs),lsof输出可能缺失 namespace 相关条目; - 最常被忽略的一点:
lsof输出默认包含列头(COMMAND PID USER …),用管道统计时务必先grep -v "COMMAND"或用-F格式化输出避开。
真正要确认“进程到底用了多少 fd”,以 ls -1 /proc/PID/fd/ 为准;要用 lsof,就专注看它擅长的事:每个 fd 对应什么资源、路径是否异常、有没有 deleted 文件、端口状态是否符合预期——别让它干它不承诺干的事。


















