lsof —— 查看进程打开的文件 / 套接字 / 句柄
三轨导读:本页是「深度实战与原理」版;同命令其他版本:命令选项速查。
这个工具是做什么的
lsof = list open files。Linux 下"一切皆文件",所以它能列出进程打开的普通文件、目录、设备、管道、以及网络连接。核心用途:排查 fd 泄漏、"文件/挂载点被谁占用"、进程连了哪些地址、磁盘空间被已删文件吃掉。
可以回答什么问题
| 问题 | 怎么用 |
|---|---|
| 进程打开了多少个 fd?是不是要耗尽了? | lsof -p <PID> | wc -l 对比 ulimit -n |
| 某端口被哪个进程占用? | lsof -i :8080 |
| 磁盘满了但 du 找不到大文件? | lsof +L1 找已删除但仍被占用的文件 |
| 挂载点卸载失败,谁还在用? | lsof /mnt/data 找占用进程 |
| 某进程连了哪些网络地址? | lsof -i -a -p <PID> |
| fd 泄漏是文件还是 socket? | lsof -p <PID> | awk '{print $5}' | sort | uniq -c 按 TYPE 归类 |
数据来源
- 来源文件:逐个进程读
/proc/<pid>/fd/(打开的 fd 符号链接)、/proc/<pid>/maps(内存映射的文件/库)、/proc/net/*(网络套接字),再汇总。 - 采集方式:遍历
/proc下所有(或指定)进程,对每个进程展开其 fd 与映射——是广度遍历而非单次查询。 - 由此决定的特性:① 进程和 fd 很多时较慢(遍历量大);② 看别的进程的 fd 需要相应权限(普通用户看不全,常需 root);③ 网络查询不如专用的 ss 快,但优势是能同时给出"是哪个进程的哪个 fd"。
bash
yum install lsof / apt install lsof一、按进程看
bash
lsof -p <PID> # 该进程打开的所有 fd
lsof -p <PID> | wc -l # 数 fd 数量(排查 fd 泄漏,和 ulimit -n 上限对比)
lsof -c cpu_demo # 按命令名前缀匹配(不用先查 PID)
lsof -c cpu_demo -a -d 0-99 # -a 与条件:只看 0~99 号 fd输出:
bash
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
cpu_demo 8921 derek cwd DIR 259,2 4096 131074 /home/derek/perf
cpu_demo 8921 derek txt REG 259,2 108176 262145 /home/derek/perf/cpu_demo
cpu_demo 8921 derek 3w REG 259,2 52400 262200 /tmp/test_kernel_io.tmp
cpu_demo 8921 derek 5u IPv4 54321 0t0 TCP 10.0.0.2:44512->10.0.0.9:6379 (ESTABLISHED)| 列 | 含义 |
|---|---|
FD | 描述符:cwd 当前目录、rtd 根目录、txt 程序本身、mem 内存映射(库)、0/1/2 标准流;数字后缀 r只读 w只写 u读写(如 3w、5u) |
TYPE | REG 普通文件、DIR 目录、CHR/BLK 字符/块设备、FIFO 管道、unix Unix域套接字、IPv4/IPv6 网络 |
DEVICE | 设备号 |
SIZE/OFF | 文件大小或当前偏移 |
NODE | inode 号(网络行显示协议) |
NAME | 文件路径 / 网络地址(本地->对端 (状态)) |
二、按文件/目录看:谁在占用
bash
lsof /tmp/test_kernel_io.tmp # cpu_demo 场景2 写的临时文件被哪些进程打开
lsof /path/to/file # 某文件被谁占用
lsof +D /var/log # 递归整个目录下被打开的文件(遍历,较慢)
lsof +d /var/log # 只看该目录一层(不递归)三、排查"设备/挂载点忙,无法卸载"
bash
umount /mnt/data # 报 "target is busy"
lsof /mnt/data # 找出还占着该挂载点的进程
fuser -m /mnt/data # 另一种查法,-k 可直接杀(谨慎)四、网络连接
bash
lsof -i # 所有网络连接
lsof -i :8080 # 占用 8080 端口的进程(端口冲突排查)
lsof -i tcp:8080 # 限定 TCP 的 8080
lsof -i TCP -s TCP:LISTEN # 所有监听中的 TCP
lsof -i -a -p <PID> # 该进程的网络连接(-a 表条件相与)
lsof -i @10.0.0.9 # 与某 IP 的所有连接
lsof -i -s TCP:ESTABLISHED # 已建立的 TCP端口/连接排查现在多用更快的 ss;
lsof -i的优势是同时给出进程和 fd,适合"这个连接是哪个进程哪个 fd"的定位。
五、常用过滤与组合
bash
lsof -u derek # 某用户打开的文件
lsof -u ^root # 排除 root(^ 取反)
lsof -p <PID> -a -i # 交集:该进程的网络连接(多个条件默认是"或",-a 变"与")
lsof +L1 # 链接数(link count)为 0 的文件 = 已删除但仍被进程占用
lsof | grep deleted # 同上,另一种找法
lsof -nP # -n 不反解主机名、-P 不转端口名 → 更快、地址更清晰六、关键指标速查
| 指标 | 缩写/英文 | 命令 | 正常值 | 异常信号 |
|---|---|---|---|---|
| fd 总数 | FD Count | lsof -p <PID> | wc -l | 远小于 ulimit | 接近 ulimit → 即将 EMFILE 崩溃 |
| fd 上限 | FD Limit / nofile | ulimit -n | 视业务(默认 1024) | 太小 → 高并发服务必然不够 |
| socket 数 | Socket Count | lsof -p <PID> | grep -c sock | 视业务 | 持续增长 → socket fd 泄漏 |
| 已删除文件占用 | Deleted Files | lsof +L1 | 0 | 有记录 → 空间未释放(文件删了但进程还开着) |
| 端口占用 | Port Occupancy | lsof -i :8080 | 一个进程监听 | 多个进程 → 端口冲突或子进程继承 |
| 按类型归类 | FD by Type | lsof -p <PID> | awk '{print $5}' | sort | uniq -c | 视业务 | REG 越来越多 → 文件泄漏;IPv4 越来越多 → socket 泄漏 |
七、常见排查方法与分析
7.1 fd 泄漏 → EMFILE
bash
# 程序报 Too many open files
lsof -p <PID> | wc -l # 看当前 fd 数
ulimit -n # 看上限
# 按类型归类看是哪种泄漏
lsof -p <PID> | awk '{print $5}' | sort | uniq -c | sort -rn
→ IPv4/v6 多 → socket 泄漏
→ ss -tanp | grep <PID> → 看是否有大量 CLOSE-WAIT
→ REG 多 → 文件打开忘了关
→ lsof -p <PID> | grep REG | awk '{print $NF}' | sort | uniq -c
找出被重复打开最多的文件路径7.2 磁盘满了但 du 找不到大文件
bash
df -h → 分区 Usage 接近 100%
du -sh /* 2>/dev/null | sort -rh → 找不到对应大文件
→ lsof +L1 → 显示 link count 为 0 的文件
→ lsof | grep deleted | sort -k7 -rn | head
→ 找出占空间最大的已删除但未释放的文件
→ 重启持句柄的进程或 echo > /proc/<PID>/fd/<N> 清空7.3 挂载点卸载失败
bash
umount /mnt/data → "target is busy"
→ lsof /mnt/data → 谁还在占用
→ 关闭对应进程或 cd 到其他目录7.4 端口被占用
bash
# 服务启动报 EADDRINUSE
lsof -i :8080 → 找出占用端口的进程
→ kill PID 或改端口
# 或
ss -tlnp | grep :8080 → 更快(推荐先用 ss)7.5 fd 使用趋势监控
bash
# 用脚本持续监控 fd 是否泄漏
while true; do
echo "$(date) $(lsof -p <PID> | wc -l)"
sleep 60
done
# 如果 fd 数单调增长且不回落 → 泄漏确认八、交叉引用
- 连接状态:ss(比 lsof -i 更快,但 lsof 能关联进程 fd)
- 进程资源:procfs §二(
/proc/<pid>/fd/是 lsof 的数据源) - 系统调用追踪:strace(open/socket 调用与 close 不配对 → 泄漏根源)
- 资源限制:kernel-tuning §四(ulimit/nofile 配置)
九、两个高频实战
① fd 泄漏:进程报 Too many open files(EMFILE)。
bash
lsof -p <PID> | wc -l # 当前 fd 数
ulimit -n # 该进程 fd 上限
lsof -p <PID> | awk '{print $5}' | sort | uniq -c | sort -rn # 按 TYPE 归类,看是 socket 还是文件泄漏socket 多 → 常配合 ss state close-wait 确认是连接没关。
② 磁盘满了但 du 找不到大文件:
bash
df -h # 显示某分区满了
du -sh /* 2>/dev/null # 却找不到对应大文件
lsof +L1 # 元凶:文件被删了但进程还开着句柄,空间不释放
lsof | grep deleted | sort -k7 -rn | head # 找出占空间最大的已删文件→ 重启(或让进程关闭)持有句柄的进程,空间立即释放。
总结
lsof 是“谁占着这个 fd/端口/文件”的定位器:它遍历 /proc/<pid>/fd、maps 和 /proc/net,能同时给出资源和所属进程,四大高频场景是 fd 泄漏(对比 ulimit -n)、端口被占(lsof -i,或先用更快的 ss)、挂载点忙、以及“磁盘满却 du 不到”的已删除文件(lsof +L1)。