ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

lsof命令详解:从端口占用到文件描述符的Linux排查利器

lsof命令详解:从端口占用到文件描述符的Linux排查利器 1. lsof到底是什么从“一切皆文件”说起干过几年Linux运维或后端开发的人一定有过这样的经历明明服务还在跑端口就是起不来明明rm删了日志文件磁盘空间却纹丝不动明明进程没崩业务却报“Too many open files”。这些问题有个共同点——都需要搞清楚“到底是谁动了我的文件、我的端口、我的资源”。这时候lsof就是最趁手的排查工具。lsof的全称是List Open Files直译过来就是“列出所有进程打开的所有资源”。它的核心逻辑建立在一个非常重要的设计理念上在Linux世界里一切皆文件。普通文件是文件目录是文件网络套接字是文件管道是文件设备节点也是文件甚至共享内存、信号量这种IPC对象也以文件描述符的形式存在。所以只要能查清楚“进程打开了哪些文件”就等于查清楚了进程所占用的全部资源。lsof之所以叫“全资源利器”是因为它的信息源直接来自内核的/proc文件系统。当你执行lsof时它会遍历系统中所有进程读取每个进程的/proc/PID/fd目录和/proc/PID/maps等接口把文件描述符逐一解析成可读的文件路径、协议信息、设备号、大小等。换句话说lsof不是“拦截”文件的打开动作而是事后“翻账单”把内核已经记录好的信息翻译给你看。这就是它能做到“无需客户端、无需额外服务、不依赖被排查进程的任何配合”的根本原因。和同类的几个命令相比lsof的定位更加全面。fuser通常用于“谁占用了某个文件或端口”ss和netstat主要专注网络连接而lsof既能看网络也能看普通文件、目录、设备、IPC覆盖面最广。你在生产环境排障时往往一个lsof就能顶好几个命令的组合拳。提示lsof依赖系统内核的/proc接口和动态库几乎所有的Linux发行版都会预装或可通过包管理器快速安装CentOS/RedHat系用yum install lsofUbuntu/Debian系用apt install lsof。2. lsof高频用法与参数拆解干运维必须掌握的组合拳2.1 从最简单的“查看所有打开文件”说起不加任何参数直接执行lsof命令会尝试把整个系统所有进程打开的每一个文件都列出来。以一台运行中的服务器为例输出可能多达几万行甚至几十万行内容大致长这样COMMAND PID TID TASKCMD USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root cwd DIR 253,1 4096 2 / systemd 1 root rtd DIR 253,1 4096 2 / systemd 1 root txt REG 253,1 1624520 133958 /usr/lib/systemd/systemd sshd 1234 root mem REG 253,1 14444032 265138 /usr/lib64/libc-2.17.so sshd 1234 root 3u IPv4 1234567 0t0 TCP *:22 (LISTEN) nginx 5678 www-data 6u IPv4 8901234 0t0 TCP 10.0.0.8:80-192.168.1.100:54321 (ESTABLISHED)这里每一列的含义很明确COMMAND是进程名PID是进程号USER是运行用户FD表示文件描述符以及打开方式cwd是当前工作目录txt是程序可执行文件mem是内存映射文件数字加u/r/w表示文件描述符编号和读写权限TYPE是文件类型DIR目录、REG普通文件、IPv4网络套接字、CHR字符设备等NAME则是实际的路径、协议或连接信息。不过说句实在话生产环境里很少有人直接全量执行不带参数的lsof因为输出量太大分析效率极低。恰恰相反lsof真正的价值在于组合过滤。你用它时几乎总要带上一个或多个定位条件让输出范围收敛到几十行以内这样才有排障意义。2.2 高频参数速查表我整理了一份自己平时用得最多的参数清单按使用频率排序新手可以直接照抄参数作用说明典型使用示例-i按网络连接过滤支持端口、IP、协议lsof -i:8080只看8080端口-p按PID查看某进程打开的文件lsof -p 1234查看PID 1234的资源-u按用户过滤lsof -u www-data查看某用户进程打开的文件-c按进程名前缀过滤lsof -c nginx查看以nginx开头的进程-t只输出PID不输出其他字段lsof -t -i:8080只看占用8080端口的PIDD递归列出某目录下被打开的文件lsof D /var/log查看日志目录被谁占着-d按文件描述符编号过滤lsof -p 1234 -d 1,2,3只看前三个fd-a多个条件取“与”关系lsof -a -u root -i:22root用户22端口-n不做DNS反向解析加快速度lsof -nP -i:80不解析主机名和端口名-P不把端口号换成协议名称配合-n使用避免把80显示成http这里特别说一下-a的用法。lsof里如果给了多个筛选条件默认是“或”的关系也就是说lsof -u root -i:22会把“root用户打开的所有文件”和“22端口相关的所有连接”都列出来这通常不是你想要的结果。想表达“同时满足”的与关系一定要显式加上-a参数写成lsof -a -u root -i:22它才会去交集。2.3 网络排查三件套端口、连接、监听网络相关是lsof用得最频繁的场景。端口被占用、连接数异常、某个服务连不上对方这些问题用一套组合就能搞定。最常见的场景是“8080端口被谁占了”。不要犹豫直接执行lsof -i:8080输出会告诉你哪个进程、哪个PID在监听这个端口甚至能看到已建立的连接。如果只想拿PID去做后续处理用-t参数lsof -t -i:8080这个命令只输出一个数字干净利落。在脚本里可以直接套用kill -9 $(lsof -t -i:8080)来强制释放端口——当然生产环境建议先看清楚这个进程是不是该杀的别误杀了重要服务。想看某个IP地址相关的全部连接用lsof -i192.168.1.100还可以叠加协议只要TCP的lsof -iTCP192.168.1.100这里面有个小坑我要提醒一下。lsof默认会把80端口显示成http、443显示成https这种“服务名”这本来是为了可读性但排查时反而容易误导。加上-P参数可以让它直接显示数字端口号加上-n可以跳过DNS反向解析既准确又提速lsof -nP -i:443我个人的习惯是凡是排查网络问题一律带上-nP除非你需要看域名信息。3. 实战复盘四个高频故障排查直接抄作业3.1 场景一端口被占用“Address already in use”这是最经典的问题。比如你启动一个Spring Boot应用默认端口8080结果报错BindException: Address already in use遇到这个报错第一时间运行lsof -i:8080输出中会有一行处于LISTEN状态的记录进程名和PID都写得明明白白。比如看到java 2345 user 123u IPv4 ... TCP *:8080 (LISTEN)那就说明PID 2345的Java进程占着这个端口。接下来怎么处理取决于业务情况。如果确认这个进程可以停就执行kill 2345等几秒再确认端口是否释放要是正常kill杀不掉再考虑kill -9 2345。这里我建议先kill后kill -9因为kill -9直接让内核强制终止进程不给进程任何清理资源的机会极端情况下会留下临时文件残留或未落盘的数据丢失能优雅退出就尽量优雅退出。还遇到过一种比较隐蔽的情况端口本身没有LISTEN记录但lsof能查到一堆TIME_WAIT或CLOSE_WAIT状态的连接。此时端口并没有被“占用”只是大量连接处于等待状态通常是应用没有正确关闭连接导致的。这时候lsof -i:8080能帮你看到协议栈里这些滞留的连接都归属于哪个PID、远端是哪里方便你从根源上去排查连接泄漏的bug。3.2 场景二磁盘空间100%但根本找不到大文件某天登录服务器执行df -h发现根分区使用率达到了98%甚至100%可你执行du -sh /一层层往下找却始终找不到占空间的大文件。这种“空间神秘消失”的场面十有八九是某个进程正在写一个已经删除的文件。理解这个现象需要一点Linux文件系统的基础。当进程打开一个文件时文件描述符指向inode当外界执行rm删除它时只是把目录项和inode的硬链接计数减一。如果这个文件正被某个进程打开着inode并不会被真正回收直到该进程关闭文件描述符或退出为止。也就是说文件看不见了但磁盘空间仍然被占用着。这时候用lsof找出“幽灵文件”lsof | grep deleted输出里会有一行类似java 5678 root 15w REG 253,1 1073741824 345678 /var/log/app.log (deleted)的记录。这说明PID 5678的进程还在持续向这个已删除的日志文件写入数据文件大小已经达到1GB。处理方案有两个。一个是重启或让进程重新打开日志文件绝大多数情况下这个办法最彻底另一个是用 /proc/5678/fd/15直接“清空”那个已打开的文件描述符指向的文件不需要重启进程就能释放空间。这个操作我试过几次对临时排障非常管用但要注意它只是把文件内容截断成0字节进程对文件描述符的引用依然存在写入还会继续。写日志的场景建议优先考虑配置日志轮转logrotate从机制上避免这个问题。3.3 场景三查看一个进程到底打开了哪些文件有时业务程序表现得异常你想知道它是不是误操作了某些路径或者确认它有没有正常加载配置文件。这时候lsof -p就派上用场lsof -p 1234输出会把PID 1234当前打开的所有文件都列出来包括工作目录、可执行程序本身、加载的共享库、配置文件、日志文件、网络连接、管道等。这里面信息量很大排查时可以重点看几类TYPE为REG的路径确认是不是读到了预期配置文件TYPE为IPv4/IPv6的记录看进程在跟谁通信cwd这一行看进程当前工作目录是否被切换过FD一列中带w的看出没往不该写的地方写东西。我踩过一个印象很深的坑某个应用明明改了新的配置文件路径重启后却还是在用旧配置参数。用lsof -p一查发现进程加载的配置文件仍然是老的绝对路径原因是启动脚本里环境变量覆盖了配置文件设置的路径。这类问题如果不看实际打开的fd光靠读代码很难定位。3.4 场景四查看某个目录下哪些文件正被占用当你想卸载一个挂载点、删除一个分区目录或者重命名某个目录时系统会提示“device is busy”或者“target is busy”。这种占用往往不像端口那样显眼得靠lsof把占用者揪出来。lsof D /data这个命令会递归列出/data目录下所有正被进程打开的文件一眼就能看到是哪个进程在占用。比如想卸载/data这个挂载点但umount /data报错先跑一下lsof D /data你会看到类似bash 1234 user cwd DIR 253,1 4096 2 /data的输出说明有shell进程把/data当作当前工作目录切走目录或结束该shell进程后umount就能顺利执行。需要注意D对大目录有点慢因为它是递归扫描的。如果目录非常深、文件非常多可以考虑用lsof /data直接看目录本身或者先缩小范围再递归。运维场景下我倒是觉得慢一点也能接受毕竟这种“busy”问题不是高频操作。4. 输出字段解读与提效技巧别让lsof的输出吓到你4.1 字段详解15分钟看懂一行输出lsof的输出列名常见的有COMMAND、PID、TID、USER、FD、TYPE、DEVICE、SIZE/OFF、NODE、NAME。对于新手来说最困惑的往往是FD、TYPE和NAME这三列我拆开讲。FD列表示文件描述符和打开方式。数字部分就是文件描述符编号后面跟着的字母含义比较丰富r只读打开w只写打开u读写打开空格未知或正在获取cwd当前工作目录不仅是文件它是目录txt程序的可执行文件对应mmap执行段mem内存映射文件DEL已经被删除但还没释放的文件映射NOFD无法获取文件描述符信息TYPE列则代表文件对象的类型。最常见的有REG普通文件、DIR目录、CHR字符设备如终端/dev/pts/1、BLK块设备、IPv4/IPv6网络套接字、unixUnix域套接字、FIFO管道、sock通用套接字。通过TYPE你可以在瞬间判断“这行记录是文件还是连接还是通道”排障时很有用。NAME列虽然叫“名字”实际内容取决于TYPE。REG文件显示完整路径IPv4连接显示本地地址、端口、远端地址、端口和连接状态比如10.0.0.8:80-192.168.1.100:54321 (ESTABLISHED)unix套接字则显示套接字路径或抽象名。NAME里的箭头很有讲究-表示一条已建立的连接没有箭头只显示*:80 (LISTEN)的就是监听套接字——注意监听套接字和已建立的连接是两种不同的fd它们会分别单独显示。4.2 几个能直接提高处理速度的小习惯第一条能不解析DNS就不解析。生产环境的服务器上DNS解析有时会超时导致lsof命令卡顿好几秒。加-n参数禁止DNS反向解析加-P直接显示端口数字而非服务名。排查网络时固定使用lsof -nP -i:端口这是我个人最推荐的习惯。第二条用-t把输出变成纯PID。这个参数特别适合脚本化处理省去了用awk或cut去抠PID的环节。比如要杀掉所有占用8080端口的进程直接写kill $(lsof -t -i:8080)如果担心有多个PID可以先lsof -t -i:8080确认列表无误再执行。在自动化脚本里这招非常省事。第三条组合-a取交集避免输出爆炸。比如查“root用户打开的22端口连接”lsof -a -u root -i:22不加-a的话输出会把root所有文件和所有22端口相关的内容混在一起干扰判断。第四条关注权限问题。普通用户执行lsof时只能看到自己有权访问的进程信息想看所有用户所有进程的完整信息需要root权限。所以生产环境上排障建议直接切换到root或使用sudo否则因为权限不足看不到列出的完整记录反而会漏判。5. 常见问题与排查技巧实录5.1 lsof提示command not found怎么办有些精简安装的容器镜像或最小化系统不带lsof。此时直接安装即可# Debian/Ubuntu apt-get update apt-get install -y lsof # CentOS/RedHat yum install -y lsof # Alpine apk add lsof如果实在无法安装也有替代方案端口占用可以用ss -lntp来查进程打开的文件可以用ls -l /proc/PID/fd来手工查看。但论便捷程度还是lsof更胜一筹。5.2 lsof执行很慢、卡顿怎么办前面提过不加任何参数全盘扫描时lsof要遍历系统所有进程的/proc信息进程多、文件多时延迟很高。所以应对办法很简单尽量带上明确的过滤条件比如-p、-i、D只扫描需要的部分。另外加-n跳过DNS解析也能显著提速。如果你确实需要全量快照可以考虑在服务器低峰期把lsof执行结果重定向到文件再慢慢分析lsof /tmp/lsof_snapshot.txt注意快照文件里可能包含一些敏感路径和连接信息分析完记得及时清理。5.3 为什么lsof看不到某些进程的某些文件最常见的原因是权限不足。普通用户执行lsof受限于内核的ptrace权限检查和/proc节点的可见性很多其他用户的进程信息是拿不到的。另一个原因是lsof版本和内核版本不匹配解析/proc信息时某些新类型对象识别不了会显示“unknown”之类的占位符。遇到strace、gdb等调试工具附加到进程上的情况lsof还可能在读取过程中看到调试器映射的临时文件但这属于正常现象不是程序bug。如果你碰上容器环境情况又复杂一些。在容器里执行lsof看到的主要是容器自己命名空间范围内的文件列表想查宿主机的全局列表需要到宿主机上执行或者给容器挂载宿主/proc目录。这个点在做容器化排查时容易踩坑我提醒一下。5.4 与ss、netstat配合快速判断网络故障lsof在网络排查上是利器但它的强项是“按进程回查资源”。如果你需要看更详细的TCP状态统计、队列长度、重传信息建议和ss搭配使用。比如# 看8080端口的监听和连接状态比netstat快、准 ss -lntp | grep 8080 # 看某IP的连接汇总 ss -ant | grep 192.168.1.100 | wc -llsof告诉你“这个端口被谁占着”ss告诉你“网络栈层面的状态究竟如何”两者互补。排查顺序上我一般先用lsof找到可疑进程再用ss确认网络细节或者反过来先用ss发现异常端口再用lsof定位到具体进程。5.5 别忽略“FDDEL”这种隐藏信号DEL状态意味着文件已经被删除但是仍然有进程在引用它。这也是我们在3.2节“幽灵文件”里看到的信号。但要注意DEL状态不一定只出现在普通文件上也可能出现在共享内存对象/dev/shm下的临时文件或临时映射上。排查时如果看到很多DEL条目可以关注一下它们是否都集中在同一个进程上如果某个进程积累了大量的DEL文件很可能是它频繁创建临时文件但释放不及时长期运行下去会消耗大量磁盘空间和内存映射资源。6. 复盘把lsof放进日常工具箱说了这么多lsof其实不是一个特别复杂的命令它的强大源于它的信息维度和过滤组合。我自己的习惯是把lsof的常用组合抄在一张便签上贴在Shell配置文件里作为别名比如alias portlsof -nP -i alias lsof_pidlsof -p alias deletedlsof | grep deleted日常排查时输一个别名就能快速出手。另外学lsof时不要孤立地背参数最好把它和/proc文件系统、文件描述符、inode这些基础概念放在一起理解。一旦你理解了“一切都是文件”的内核设计lsof的很多用法就是水到渠成的事不用靠死记硬背。最后分享一个我总结的经验真正熟练的运维不是拿lsof去应对所有问题而是知道什么时候该用lsof、什么时候该用fuser或ss。lsof的输出信息密度高适合从文件视角回溯进程行为fuser更适合“快速确认谁在占用指定文件”ss适合深挖网络状态。工具用得准比工具多更重要。希望这篇文章能帮你把lsof这个命令变得顺手下次遇上“端口起不来”“磁盘清不掉”“目录卸不掉”这些烦心事你能第一反应想起来先跑一个lsof看看。
返回列表