当前位置:首页>Linux>每天学一个Linux命令系列(33):lsof - 文件删不掉?端口被占?lsof出马

每天学一个Linux命令系列(33):lsof - 文件删不掉?端口被占?lsof出马

  • 2026-09-08 11:38:52
每天学一个Linux命令系列(33):lsof - 文件删不掉?端口被占?lsof出马
运
运维少年 · 科技观察

这个命令是干啥的

lsof 全称是 List Open Files。在 Linux 里"一切皆文件",所以 lsof 能列出来:普通文件、目录、网络 socket、管道、设备文件,只要是进程打开的东西都能查。

我第一次被 lsof 救命的场景是这样的:有个程序要监听 8080 端口,但启动时报 Address already in use。我 df、netstat 一顿操作猛如虎,发现端口确实被别人占了,但不知道是谁。老同事过来敲了个 lsof -i :8080,直接显示了 PID 和进程名,干净利落。从那天起 lsof 就是我的必装工具。

lsof 最大的好处是:它能告诉你"谁在用这个东西"。不管是文件删不掉、端口被占、磁盘卸载不了,lsof 一出马,总能找到元凶。

基本用法(3分钟上手)

查看哪个进程占用了端口

# 查看 8080 端口被哪个进程占用
lsof -i :8080

输出大概是这样的:

COMMAND   PID   USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
java     12345 ubuntu   56u  IPv4  123456      0t0  TCP *:8080 (LISTEN)

COMMAND 是进程名,PID 是进程 ID,USER 是运行用户,NAME 显示是监听状态。一目了然。

# 查看所有监听中的端口
lsof -i -s TCP:LISTEN

查看文件被哪个进程占用

# 查看某个文件正在被谁使用
lsof /var/log/nginx/access.log

删文件的时候提示 Device or resource busy,用这个查一下就知道是哪个进程还在用。

查看某个用户打开的文件

# 查看 ubuntu 用户打开了哪些文件
lsof -u ubuntu

这个命令输出一般很多,通常会配合 grep 过滤:

# 查看指定用户打开的 TCP 连接
lsof -u ubuntu -i TCP

查看某个进程打开了哪些文件

# 查看 PID 为 12345 的进程打开了哪些文件
lsof -p 12345

这个特别实用。比如你的 Java 程序内存泄漏,怀疑是文件句柄没释放,用这个一看就知道。

进阶骚操作

递归查找目录下被打开的文件

# 递归查找 /var/log 目录下所有被打开的文件
lsof +D /var/log

+D 会递归遍历整个目录,比直接用 lsof /var/log/* 方便多了。注意如果目录下文件很多,输出会很大。

查看已删除但未释放的文件

这是我最常用的技巧。有时候你用 df -h 看到磁盘满了,但是 du -sh /* 加起来又没那么多,说明有一个被删掉的文件还在被占用。

# 查找已删除但进程还在用的文件
lsof | grep '(deleted)'

输出里会看到类似这样的行:

nginx   12345  root   txt  REG  8,1   0   123456 /usr/sbin/nginx (deleted)

这种文件已经被删除,但进程打开的文件描述符还没关,所以磁盘空间没真正释放。解决方案是重启进程或者 > /proc/PID/fd/FD 清空。

查看网络连接详情

# 查看所有 IPv4 的网络连接
lsof -i4

# 查看所有 IPv6 的网络连接
lsof -i6

# 查看所有 TCP 连接
lsof -i TCP

# 查看所有 UDP 连接
lsof -i UDP

# 排除 22 端口(SSH)
lsof -i TCP -s TCP:LISTEN -u ^root

-s TCP:LISTEN 只显示监听的端口,^root 表示排除 root 用户的进程。

查看进程的当前工作目录

# 查看进程的当前目录
lsof -p 12345 | grep cwd

cwd 就是 current working directory。有时候你想知道一个后台进程是在哪个目录启动的,用这个查。

查看进程打开了哪些库文件

# 查看进程加载的动态库
lsof -p 12345 | grep '\.so'

查动态库版本、库文件路径,定位依赖问题的时候很有用。

避坑指南

1. lsof 输出太大

没有过滤条件直接跑 lsof,输出可能上万行,而且很慢。系统上有几百个进程,每个可能打开几百个文件描述符。所以一定要加过滤条件,要么 -i、要么 -p、要么 grep。

2. 权限问题

普通用户只能看到自己进程的文件。想查所有进程,需要 root 权限:

# 非 root 用户查其他进程会返回空或者报错
sudo lsof -i :80

3. 内核线程和进程的区别

# lsof 会显示内核线程(kernel thread),它们没有用户态文件描述符
# 可以通过 -K 排除
lsof -K

Linux 内核线程也有 PID,但它们的文件信息没有意义,-K 可以排除。

4. 文件描述符类型区分

lsof 输出里的 FD 列:
- cwd 当前工作目录
- rtd 根目录
- txt 程序代码文件
- mem 内存映射文件
- 0u、1u、2u 分别对应 stdin、stdout、stderr

TYPE 列:
- REG 普通文件
- DIR 目录
- IPv4 或 IPv6 网络 socket
- unix Unix domain socket

5. 不要在繁忙系统上跑裸 lsof

# 不要这样,会卡死终端
lsof

# 要这样,加限定
lsof -i :3306

我之前在一个 64G 内存、上千个连接的生产数据库上跑了 lsof,结果等了 30 秒才出结果,而且 CPU 飙到 100%。从此以后必加过滤。

实战场景

场景一:端口被占,快速定位凶手

上线新服务,报错 bind: address already in use:

# 快速找到端口占用者
sudo lsof -i :8080

输出显示 PID 是 12345,是个 Java 进程。然后决定是杀了重来还是换个端口:

# 杀进程
kill -9 12345

# 或者确认一下这个 PID 是谁的
ps -ef | grep 12345

场景二:磁盘空间满了但 df 和 du 对不上

# 先看 df
df -h
# 发现 /var 用了 95%,但 du -sh /var/* 加起来只有 60%

# 查被删除但未释放的文件
sudo lsof | grep '(deleted)'

找到后,确认是哪个文件:

# 确认文件对应的进程和文件描述符号
sudo lsof +L1

+L1 显示 link count 小于 1 的文件,也就是被删掉的文件。然后要么重启进程,要么清空文件描述符:

# 清空被删文件占用的空间(不会删除进程正在写的文件)
: > /proc/PID/fd/FD_NUMBER

场景三:Nginx 日志轮转后还在写旧文件

做日志轮转后,发现旧日志文件的大小还在增长,说明 Nginx 还在往里写:

# 查看 nginx 进程打开了哪些日志文件
lsof -c nginx | grep log

输出会显示 nginx 打开的文件路径。如果日志轮转后没有 reload,它还在写旧文件的 inode。解决方法是 nginx -s reload。

场景四:外部存储卸载失败

# USB 盘或者 NFS 挂载点卸载不了
umount /mnt/usb
# 报错: target is busy

# 查谁在用
lsof /mnt/usb

找到后,要么关掉对应的进程,要么加 -l 强制卸载。

今日作业

你的服务器 /tmp 目录空间告警,但用 du -sh /tmp/* 找不到太大的文件。请用 lsof 查找:
1. 在 /tmp 目录下被打开的文件
2. 特别关注标记为 "(deleted)" 的文件
3. 找到之后,不重启进程,用 : > /proc/PID/fd/FD 的方式释放空间
4. 确认空间是否真的释放了

写一条完整的 lsof 命令,并说明你怎么判断进程是否可以安全清理。

E N D

运维少年 · 科技观察

最新文章

随机文章