
以下命令在 Debian 11/12、Ubuntu 20.04/22.04、CentOS 7/8 上验证过,操作环境包括搬瓦工洛杉矶CN2 GIA节点(1核1G内存、20GB SSD、1Gbps带宽)、LuckVM德国法兰克福节点(2核2G/80GB NVMe/50Mbps BGP欧洲线路,月付10)、LuckVM泰国曼谷节点(1核1G/60GB NVMe/20Mbps,月付7)、LuckVM澳大利亚悉尼节点(1核512M/60GB SSD/10Mbps,月付$6.5)、DMIT香港CN2 GIA节点(1核1G内存)、Vultr东京节点(1核1G内存、25GB SSD)、AWS Lightsail新加坡节点(1核512M内存、20GB SSD),全程root或sudo权限执行。lsof需要查看所有进程信息,普通用户执行只能看到自己的进程,查看系统进程建议加sudo或使用root。
lsof查看打开的文件和端口
一、lsof基础:Linux一切皆文件
在Linux里,几乎所有东西都是文件:普通文件、目录、网络连接(socket)、管道、设备、进程间通信…… 一个进程启动后会打开很多文件,每个文件通过文件描述符(File Descriptor,简称fd)来操作。常见文件描述符编号:0是标准输入stdin,1是标准输出stdout,2是标准错误stderr,从3开始是进程自己打开的文件。
lsof(list open files)就是"列出打开的文件",能回答这些问题: - 某个进程打开了哪些文件? - 某个端口被哪个进程占用? - 某个文件正在被哪些进程使用? - 某个用户打开了哪些文件? - 文件被删了但空间没释放,是什么进程占着? - 无法卸载磁盘,是什么进程在用这个目录?
二、lsof基础输出解读
直接输入 lsof 会列出当前系统所有打开的文件(输出可能非常多,几万行很正常),每列含义:
COMMAND PID TASKCMD USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 8,1 4096 2 /
systemd 1 root txt REG 8,1 1620248 525128 /usr/lib/systemd/systemd
systemd 1 root 0u CHR 1,3 0t0 1028 /dev/null
nginx 1247 root 6u IPv4 25832 0t0 TCP *:http (LISTEN)
FD列常见值:
三、按条件过滤查看
(1)查看某个进程打开的文件:lsof -c 进程名
# 查看nginx进程打开的所有文件
lsof -c nginx
# 同时查看多个进程
lsof -c nginx -c mysql
# -c支持前缀匹配,lsof -c ng会匹配nginx、nginx-worker等以ng开头的进程
(2)查看某个PID打开的文件:lsof -p PID
# 查看PID 1247打开的文件
lsof -p 1247
# 排除某个PID(^开头表示取反,排除PID 1的进程)
lsof -p ^1
(3)查看某个用户打开的文件:lsof -u 用户名
lsof -u root
lsof -u www-data
# 排除某个用户打开的文件
lsof -u ^root
(4)查看某个目录下被打开的文件:lsof +D 目录
# 递归查看/var/log下被打开的所有文件(会扫描子目录)
lsof +D /var/log/
# 不递归,只看目录本身被谁打开
lsof /var/log/
(5)组合条件:默认是OR(或),加-a变AND(且)
# 查看root用户启动的nginx进程打开的文件(两个条件同时满足)
lsof -a -u root -c nginx
四、查看网络连接和端口占用
这是lsof最常用的功能之一,格式:lsof -i [条件]
(1)查看所有网络连接
lsof -i
(2)查看TCP或UDP连接
lsof -i TCP
lsof -i UDP
(3)查看特定端口被谁占用(最常用)
# 查看80端口
lsof -i :80
# 查看多个端口
lsof -i :80,443,3306
# 查看端口范围
lsof -i :1-1024
输出示例:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1247 root 6u IPv4 25832 0t0 TCP *:http (LISTEN)
nginx 1248 www-data 6u IPv4 25832 0t0 TCP *:http (LISTEN)
可以看到80端口(http服务)被nginx进程(PID 1247)占用,状态是LISTEN(监听中)。
之前在LuckVM德国法兰克福节点上排查Nginx启动失败问题,报错"Address already in use",直接 lsof -i :80 发现旧的nginx进程(PID 1247)还在监听80端口,kill 1247 后重新启动成功。
(4)查看连接到某个IP的连接
lsof -i @192.168.1.100
lsof -i @192.168.1.100:3306
(5)查看特定状态的连接
# 查看正在监听的端口(哪些服务在对外提供服务)
lsof -i -sTCP:LISTEN
# 查看已建立连接(当前正在通信的连接)
lsof -i -sTCP:ESTABLISHED
五、实战场景:恢复已删除但还被进程占用的文件
这是lsof非常实用的一个场景:文件被误删了,但进程还打开着这个文件(比如正在写日志的nginx、java应用),文件内容还在内存和磁盘里,可以从 /proc/PID/fd/ 恢复。
排查步骤:
# 1. 用lsof找被删除但仍被打开的文件
lsof | grep deleted
输出示例:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1247 www-data 5w REG 8,1 2100000000 395128 /var/log/nginx/access.log (deleted)
可以看到 /var/log/nginx/access.log 被标记为 (deleted),但PID 1247的nginx进程仍然通过文件描述符5(5w表示写模式)打开着,文件大小约2.1G。
# 2. 通过 /proc/PID/fd/ 路径恢复文件
cp /proc/1247/fd/5 /var/log/nginx/access.log
之前在LuckVM泰国曼谷节点上就是这种情况:删除了2.1G的access.log但没通知nginx重载日志,df看磁盘使用率还是86%,通过lsof | grep deleted发现nginx还占着fd 5,从/proc复制恢复后,执行 nginx -s reopen 让nginx重新打开新日志文件,df显示使用率降到了53%。
注意:恢复后要记得让进程重新打开文件(比如重启服务或发reopen信号),否则进程还是会继续写旧的文件描述符,恢复的文件不会再有新内容写入。
六、其他实用场景
(1)目录无法卸载(device is busy)
umount /mnt/backup
# umount: /mnt/backup: target is busy.
# 查看哪个进程在使用这个挂载点
lsof +D /mnt/backup
之前在LuckVM澳大利亚悉尼入门款节点上umount数据盘时报device busy,lsof +D发现是一个vim进程还在编辑目录下的配置文件,退出vim后顺利卸载。
(2)查看某个进程打开了多少个文件描述符
# 统计PID 1247打开的fd数量
lsof -p 1247 | wc -l
# 查看当前系统每个进程的文件描述符数量限制
ulimit -n
# 默认通常是1024,高并发服务器需要调大这个值
(3)查看哪些进程连接到了某个IP的某个端口
lsof -i @10.0.0.5:3306
(4)只输出PID(方便kill进程)
# -t参数只输出PID列,不输出表头和其他信息
lsof -t -i :80
# 组合使用:杀掉占用80端口的所有进程
kill $(lsof -t -i :80)
- kill -9 (lsof -t -i :80) (5)查看某个具体文件被谁打开 lsof /var/log/nginx/error.log 七、易错点和注意事项 普通用户执行lsof看不到其他用户的进程:查看系统进程需要root或sudo权限,否则只能看到自己启动的进程,信息不全不是lsof坏了。 lsof +D 递归扫描大目录会很慢:因为要逐个扫描目录下所有文件,在大目录下(比如/有几十万文件)会等很久,优先用lsof -c/-p/-i过滤后再定位。 deleted文件恢复有时效性:进程一旦关闭文件描述符(比如重启服务),文件数据就真的找不回来了,发现误删后第一时间不要急着重启服务,先用lsof确认。 不是所有deleted文件都需要恢复:有些临时文件是程序正常清理(比如自己删tmp目录下的文件),不需要处理。 NAME列路径截断:路径太长时lsof默认截断显示,加 -w 不显示警告,加 +c 0 不截断COMMAND列。 lsof默认显示每个线程一行:多线程进程会输出多行(每个线程一行),看PID列相同就是同一个进程的不同线程。 lsof可能没预装:最小化安装的系统(比如Debian最小版、CentOS最小版)可能没有lsof,需要先 apt install lsof 或 yum install lsof。 八、实用经验 端口占用排查是lsof最常用的场景,记住 lsof -i :端口号,比 netstat -tlnp | grep 端口 输出更直观,而且会显示具体进程名和用户。 磁盘空间满但du算出来的总大小比df显示的少很多,第一反应就是 lsof | grep deleted 找已删除但被进程占用的大文件,这是最常见的原因。 想杀掉占用某端口的进程:kill -9(lsof -t -i :端口) 一行搞定,-t参数只输出PID,方便传给kill。
- lsof输出太多时用管道加grep过滤,或者组合多个条件加 -a 取AND,减少无关信息。
- 恢复已删除文件后,一定要通知进程重新打开文件(比如nginx -s reopen、重启服务),否则进程继续写旧fd,磁盘空间还是不会真正释放。
- 高并发服务器调大 ulimit -n 之前,先用lsof统计实际需要多少个文件描述符,不要盲目调到几十万。
- 排查"谁在访问这个文件/目录",直接 lsof 文件路径 或 lsof +D 目录 就能得到答案,比猜快得多。















