遇到端口被占用的时候,我最常看到有人这么干:netstat -tunlp | grep 8080,翻dmesg,然后再挨个ps -ef去猜哪个进程才是罪魁祸首。这种连蒙带猜的排查方式,其实一行lsof就能结束战斗。
lsof全称是 list open files,直译过来就是“列出所有进程打开的所有资源”。它在Linux系统里的地位,有点像一把排查问题的瑞士军刀:端口被占了、文件删不掉、磁盘空间没释放、想知道某个进程到底在读写哪些文件、甚至想查某个用户的所有网络连接,它都能干。如果说ps是看“谁在跑”,netstat/ss是看“谁的网卡在用”,那lsof更像是把进程、文件、网络端口、用户这四张平时分散的表,按需要“join”起来一次性查清楚。这篇文章我打算从最基础的输出字段讲起,再按“查询维度”重新组织参数,然后用三个实际排查场景串一遍,最后聊聊它的原理和边界。不管是刚入门的Linux新手,还是想查漏补缺的老手,都能在这篇里找到有用的东西。
1. lsof的输出到底在说什么:先过读列这一关
很多教程上来就把参数糊你一脸,结果看完还是不知道怎么用。我的建议是先学会看输出,因为lsof几乎所有的查询结果都用同一套格式展示,把这套格式看懂了,后面用参数只是“按需过滤”的问题。
随便跑一条命令,输出大概长这样:
bash复制$ lsof -i:8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 12345 app 42u IPv4 52780 0t0 TCP *:8080 (LISTEN)
这里每一列的含义有必要交代清楚,不然你只看到一堆数字和缩写,根本不知道发生了什么。
| 列名 | 含义 | 排障时的主要用途 |
|---|---|---|
| COMMAND | 进程对应的命令名 | 快速判断是什么程序占用了资源 |
| PID | 进程ID | 后续kill、查看进程详细信息的关键 |
| TID | 线程ID,通常为空,多线程程序才显示 | 排查线程级别问题时有用,平时忽略 |
| USER | 运行该进程的用户 | 判断是系统服务还是业务进程 |
| FD | File Descriptor,文件描述符 | 最重要的一列,见下方详细拆解 |
| TYPE | 资源类型 | 判断是普通文件、目录、套接字还是设备 |
| DEVICE | 设备编号,格式为“主设备号,次设备号” | 一般用不到 |
| SIZE/OFF | 文件大小或偏移量 | 排查日志文件大小时偶尔有用 |
| NODE | inode节点号,网络套接字则是协议类型标识 | 理解文件系统时有用 |
| NAME | 完整的路径名、连接信息或协议状态 | 最直接可读的信息来源 |
首次接触lsof的人,卡壳的地方通常集中在FD和TYPE这两列,这两个也确实是最有“信息量”的字段。
FD列表示文件描述符以及打开模式,常见的值有这些:
cwd:进程的当前工作目录。进程启动时所在的目录,通常会持有这个目录的引用。txt:进程运行的程序文件本身,也就是二进制可执行文件。mem:内存映射文件,通常是动态链接库(.so)或者可执行文件被映射到内存中的部分。rwd:以“读-写”模式打开并带有“截断”标记,多用于日志文件。1u、2u:标准输出(stdout)和标准错误(stderr),u代表读写模式。DEL:文件已被删除,但还被进程占用,典型的表现就是磁盘空间不释放。
TYPE列则标识资源类型:
REG:普通文件。DIR:目录。CHR:字符设备,比如终端设备。BLK:块设备,比如磁盘。unix:Unix域套接字,常用于本机进程间通信。IPv4、IPv6:网络套接字。FIFO:命名管道,进程间通信的一种方式。
判读FD和TYPE组合的方式,举个例子你就明白了。假如输出里有一行是:
code复制nginx 3876 www-data 9u IPv4 0t12345 TCP 127.0.0.1:80->192.168.1.10:54321 (ESTABLISHED)
意思是:用户www-data运行的nginx进程(PID 3876)通过IPv4网络,从本机127.0.0.1的80端口,连到了`192.168.1
