ARTICLE DETAIL

资讯详情

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

Linux运维三剑客:ps、ss、lsof搞定端口占用与进程排查

Linux运维三剑客:ps、ss、lsof搞定端口占用与进程排查 深夜两点半您在服务器上部署新版本服务启动命令一敲控制台却冷冷地吐出一行Address already in use。这是Linux运维人最熟悉也最讨厌的瞬间——端口被占了。可端口只是个数字它背后站着的不是一个端口而是一个进程、一个socket、一个可能正在干活的程序。这时候该找谁ps、ss、lsof这三条命令就是Linux进程与端口世界里公认的“三剑客”也是运维人的火眼金睛。这篇总结既写给刚入行、一查到端口占用就慌着kill -9的新手也写给排查进程异常、端口不通时经常漏掉关键步骤的进阶伙伴。看完您能直接上手一套从进程查端口、从端口查进程、判定能不能杀、验证通不通的方法论关键时刻能救命。1. 为什么是这三把刀ps、ss、lsof的分工逻辑1.1 一个进程和一个端口是同一件事的两张脸要理解这三条命令为什么能组成“三剑客”得先想明白一个底层事实进程和端口不是两个独立的东西而是同一个网络程序的两张脸。一个程序跑起来在内核里成为一个进程有唯一的PID占用CPU、内存这是它的“本体”如果这个程序需要对外提供网络服务它就得申请一个socket然后把这个socket绑定到一个IP和端口上调用listen开始监听。这时候端口就成了这个进程在网络上的一扇门。别人能访问你的服务不是因为端口自己会干活而是因为有一个进程站在端口后面不断处理进来的连接。所以排查“端口被占”这件事表面是查端口本质是查进程反过来排查“进程怎么卡住了有时也得先看它占着哪些端口、开了哪些文件。ps管进程视角ss管端口和连接视角lsof管两者之间的关联视角——三把刀各管一摊合起来才是一条完整的排查链。1.2 三剑客的选用原则与定位对比先说结论再详细展开。以下是我日常排查时的分工习惯工具所属工具集负责视角典型问题最常用姿势psprocps进程本身进程在不在、什么状态、占多少资源、谁的孩子ps -ef、ps aux、ps -eo pid,ppid,stat,cmdssiproute2端口与连接端口谁在监听、连接什么状态、队列有没有堆积ss -tlnp、ss -tunaplsoflsof套件进程与端口/文件的关联端口背后是哪个进程、进程开了哪些socketlsof -i:8080、lsof -p PID可能有人会问为什么没有netstat不是它不行而是它在多数现代发行版里已经被ss替代。netstat来自net-tools很多命令已经停止维护ss来自iproute2包它读取的是内核inet_diag接口直接问内核拿数据速度比netstat这种解析/proc/net的方式快得多在连接数上万时差距尤其明显。当然netstat -antp在很多老系统上依然是救命的我不反对大家继续用但新装系统优先练ss。1.3 什么时候需要三剑客合体很多人每个命令都背得滚瓜烂熟可真遇到事故却不知道该先用谁。我的经验是不要按命令背要按场景记忆。服务起不来、报“端口被占用”先用ss -tlnp看端口所属进程再用ps -fp PID确认这个进程是什么、能不能动。服务起来了但连不上先ss -tlnp确认服务确实在监听再确认监听地址对不对是0.0.0.0还是127.0.0.1再测通不通。某进程把CPU打满、或者怎么都杀不死先用ps看状态和父进程再考虑是不是进入了D状态或是不是僵尸进程。怀疑进程有异常外联或者后门用ss -tunap查所有TCP连接再用lsof -p PID看这个进程开了哪些不该开的socket。这三个命令加起来覆盖了“进程活着吗→端口开着吗→这俩到底谁连着谁”的全部排查链路。下面我从第一剑正式开始每一剑都会带上输出样例和我的读法保证看完就能对着终端敲。2. 第一剑ps——把进程看穿2.1 核心字段读法PID、PPID、STAT、TIME、COMMANDps可能是Linux里被人用最多、也误解最多的命令。很多人只会ps -ef然后调grep其实关键是读懂输出字段的意义。最常用的两条是ps -ef和ps aux。ps -ef格式偏BSD风格ps aux格式偏SysV风格两者显示的信息略有差异但对日常排查来说我更推荐一条自定义命令ps -eo pid,ppid,user,stat,lstart,time,cmd逐个讲一下重点字段PID进程ID查端口、杀进程、看日志都靠它。PPID父进程ID这是最容易被忽略但最值钱的字段。进程被谁拉起来的PPID一查便知。如果是1号进程systemd收养的说明它已经是孤儿进程如果是某个Shell拉起来的说明它还在被会话持有。STAT进程当前状态。这个字段我下面单独讲几乎一半进程故障都写在它上面。lstart进程启动时间。判断一个进程是“刚被拉起来的老配置”还是“真正历史遗留”就看它跟业务发版时间对不对得上。TIME进程累计占用的CPU时间注意这不是运行时长而是所有线程消耗CPU时间的总和。它和%CPU结合能判断进程是不是“一直在干活”。CMD完整命令行注意可能包含参数。同一个程序启动方式不同CMD差异巨大。另一个实用姿势是通过pgrep和pidof快速找进程IDpgrep -af java pidof nginxpidof只能按程序名匹配pgrep更灵活-a还能同时打印完整命令行。这两个命令在脚本里比ps | grep干净得多不会出现grep自己匹配自己的尴尬。2.2 STAT状态机运行、睡眠、不可中断、僵尸ps输出的STAT字段看着像乱码其实是很有信息量的编码。Linux进程的主要状态对应关系如下STAT字符含义常见场景处理方式R运行中或可运行正常执行或抢不到CPU疯狂轮转看%CPU判断是正常还是死循环S可中断睡眠等待I/O或等待事件最常见正常状态D不可中断睡眠等内核I/O通常卡在磁盘或NFS非常棘手普通kill无效需排查底层I/OZ僵尸进程子进程已退出但父进程没回收检查父进程让父进程wait或直接处理父进程T停止被CtrlZ或SIGSTOP暂停kill -CONT恢复I空闲内核线程的idle状态正常第二列常出现高优先级、N低优先级、l多线程、s会话领导者等修饰符。运维里最让人头疼的两个状态是D和Z。D状态进程在等不可中断的内核I/O典型触发点是磁盘卡死、NFS挂载超时、或者某个驱动函数卡住。这种进程你kill -9都没用因为它在内核态根本没机会处理信号。我遇到过一次MySQL卡在D状态排查到最后是底层存储的LUN整个不可用。经验是看到D不要急着杀进程先去看iostat、dmesg确认是不是存储层的锅。Z状态则是子进程退出后父进程没有调用wait回收它。如果只是一两个僵尸通常不用管但僵尸进程如果积累到几百个说明父进程逻辑有bug或父进程本身就是个僵尸。处理僵尸的根本思路不是kill僵尸本身信号对它无效而是处理它的父进程——让父进程正常退出然后僵尸被systemd收养并回收。2.3 实战一个java进程到底在干什么特意把Java单拎出来因为后台服务里Java占了极大比例而Java进程的排查又有自己的独特路数。当你看到这样一个输出时$ ps -eo pid,ppid,user,stat,lstart,time,cmd | grep java 12345 1 app java -Xms2g -Xmx2g -jar app.jar --server.port8080几个判断立刻成立PPID是1说明它是孤儿进程被systemd收养lstart如果是三个月前而业务版本是三天前发版的那它大概率是没被正确重启的旧进程TIME如果已经累计了上千分钟这个进程几乎一直在烧CPU。Java进程的多线程特性在ps里也有体现STAT第二列会出现l标记。要深入看线程级情况单靠ps不够还得配合top -Hp PID或jstack PID。但在那之前ps已经告诉你“这个Java进程还活着、一直在跑、被谁拉起来”——这已经完成了事故定级的第一环。顺带说一个冷门但面试和排查都可能遇到的知识点Linux的comm字段进程名长度限制是15个字符TASK_COMM_LEN超过会被截断。你如果给进程起了一个特别长的名字用ps -C匹配会得到截断后的名称。解决办法是用prctl设置进程名或者直接把名字控制在15字符内。这不是什么高级操作但知道它能避免你在脚本匹配进程时踩坑。3. 第二剑ss/netstat——把端口看穿3.1 端口是怎么被“占”的从bind到listen先补一个常被误解的基础一个进程要监听端口内核发生了什么。程序创建一个socket调用bind把socket绑定到某个地址和端口。如果这个端口已经被别的socket独占绑定bind就会失败内核返回EADDRINUSE翻译给用户就是“Address already in use”。等bind成功后再调用listen端口才进入监听状态。这里有个很重要的细节端口冲突在bind阶段就发生了所以报错往往在服务启动瞬间出现。而SO_REUSEADDR这个socket选项允许新启动的进程绑定到一个处于TIME_WAIT状态的端口——这是服务重启频繁时能不能快速恢复的关键。很多框架默认开了这个选项所以重启没事但你如果手写socket服务又没设置它就会遇到“进程明明杀了、端口还报被占”的诡异情况。再说一个几乎所有面试都会考的点监听地址。端口和IP是绑在一起的有人问“0.0.0.0:80被占是所有地址的80端口都没法用了吗”答案不是。0.0.0.0表示监听所有IPv4地址它占的是“所有本地地址的80端口”这个组合如果另一个进程想单独监听127.0.0.1:80它会因为0.0.0.0的覆盖范围而冲突。反过来如果某个进程只监听了127.0.0.1:80那它在公网网卡上完全没占任何端口外部请求当然也连不上。顺带一提小于1024的端口叫特权端口非root用户进程默认没有权限绑定。这也是“1024端口分界线”这个说法背后的真实逻辑内核要求bind特权端口时进程必须有CAP_NET_BIND_SERVICE能力普通用户要么用root启动要么给程序加capability。3.2 ss和netstat怎么选输出怎么看先说结论推荐主用ss但两台机器都装了netstat就当备用。下面是几个高频命令# 看所有TCP监听端口和对应进程必须加-p才能看PID ss -tlnp # 看所有TCP/UDP连接带上进程 ss -tunap # 只看指定端口的监听和连接 ss -tlnp | grep 8080 # 等价的netstat写法 netstat -antp | grep 8080ss -tlnp的输出样例State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:((java,pid12345,fd29))这一段信息量极大State列当前socket状态LISTEN表示在监听ESTAB表示已建立连接TIME-WAIT表示连接正在关闭等待。Recv-Q/Send-Q接收和发送队列。对监听socket来说Recv-Q表示已完成的握手连接等待accept的数量。如果这个数字一直很大说明应用层处理不过来连接在堆积。Local Address:Port监听地址。注意区分0.0.0.0和127.0.0.1含义完全不同。Process列关键信息。内核已经给出了占用这个端口的进程PID和文件描述符编号。没有root权限时这里是空的。ss比netstat直观的另一个点它不用解析所有/proc/net文件就给出数据命令执行速度快一截。在高并发服务器上netstat -antp可能卡顿ss基本瞬间返回。3.3 几种必须认识的TCP状态LISTEN、ESTABLISHED、TIME_WAIT、CLOSE_WAIT运维排查端口问题绕不开TCP状态。我列一张最实用的表按认识优先级排序状态含义出现在哪一侧排查提示LISTEN正在监听等待连接服务端服务正常对外提供服务ESTABLISHED连接已建立数据可传输两端正常但量大时注意fd和带宽SYN_SENT主动发起连接等对方SYNACK客户端对方可能不存在或防火墙拦截SYN_RECV收到SYN等最后一次握手确认服务端可能是半连接洪泛看Recv-QTIME_WAIT主动关闭方等待2MSL确保旧包消失主动关闭方大量出现是正常现象别慌CLOSE_WAIT对方已关闭本方还没关自己的socket被动关闭方大量出现应用bug连接泄漏FIN_WAIT_2 / LAST_ACK关闭过程中的过渡态对应端少量正常堆积说明异常我最想强调两个状态。第一是TIME_WAIT。高并发短连接场景下主动关闭连接的一方会产生大量TIME_WAIT这本身就是TCP可靠关闭的机制不是故障。默认情况下它约63秒后自动消失。如果一看到几万个TIME_WAIT就急着调内核参数先冷静你的连接模型本身改一改比如用长连接复用比动net.ipv4.tcp_fin_timeout更健康。第二是CLOSE_WAIT。这个状态堆积起来几乎都是应用的问题对端已经发起了关闭但你的程序没有调用close()socket挂在半关闭状态。常见于业务代码里读取完响应后忘了关闭连接。大量CLOSE_WAIT意味着文件描述符会被耗尽最终报“Too many open files”。排查它先从ss -tanp | grep CLOSE_WAIT找到是哪些进程再看它们的逻辑有没有正确关闭连接。4. 第三剑lsof——打通进程与端口的最后一米4.1 从端口反查进程lsof和fuserps看进程、ss看端口但真要把“端口号”变成“PID进程名文件描述符”最顺手的是lsof。查8080端口被谁占用lsof -i:8080输出COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 12345 app 29u IPv6 123456 0t0 TCP *:8080 (LISTEN)这里面能直接看到进程名、PID、文件描述符号29、socket类型和监听地址。比ss多给了一个信息FD编号这对接下来lsof -p深挖很重要。fuser是另一个更“暴力”一点的反查工具fuser 8080/tcp fuser -v 8080/tcp fuser -k 8080/tcpfuser -v会打印占用的进程-k会直接发SIGKILL。我的建议是日常排查可以看fuser -v但fuser -k要非常谨慎地使用。它默认直接SIGKILL如果8080端口背后是一个正在跑业务的进程你没有确认就把它杀了后果自己体会。真要用可以先用fuser -v确认PID再用ps看这个进程是什么最后再决定怎么处理。4.2 从进程反查端口lsof -p PID反过来已知一个PID想查它到底监听了哪些端口、和哪些IP建立了连接lsof -p 12345 | grep TCP输出里能看到这个进程所有的TCP socket监听中的显示LISTEN已连接的显示对端IP和端口。这在排查“这个进程是不是在偷偷外联”“配置里写的端口到底生效没有”的时候极其有用。还有一个更狠的用法查看某个进程打开的所有文件描述符而不仅仅是socketlsof -p 12345你会看到它打开了哪些普通文件、哪些日志文件、哪些socket、哪些管道。Linux“一切皆文件”的理念在这里体现得淋漓尽致。如果一个进程的文件描述符数量异常增多lsof -p能直接告诉你它到底在死死攥着什么东西不放。4.3 为什么lsof本质上是文件系统的侦探lsof能同时查出进程和端口秘密在于/proc文件系统。Linux内核把每个进程的信息都暴露在/proc/PID/下面其中/proc/PID/fd/目录里每个打开的文件描述符对应一个符号链接。执行ls -l /proc/12345/fd你能看到这个进程所有打开的fd指向哪里——普通文件会指向路径socket会指向socket:[inode号]这样的描述。lsof就是把这些信息整理翻译成人类可读的格式。所以即便你在一台没装lsof的机器上也能用原始方式粗略侦察一个进程的socketls -l /proc/12345/fd | grep socket这个思路在排查容器、或者系统被精简过的情况时很管用也是理解lsof原理的一条捷径。4.4 常见坑权限与不可见性用lsof和ss查别的进程时如果当前用户权限不够Process列会为空lsof也只会显示你自己的进程信息。解决办法很简单用root或者用sudo。线上环境出于安全考虑通常不会给所有运维同学root权限但排查网络问题时sudo lsof -i:8080几乎绕不过去。另一个坑在容器环境在容器内执行lsof只能看到容器内进程宿主机的端口映射在容器里看不见。你看到容器内8080端口空闲但宿主机上映射的8080却被另一个容器占了——这时候得在宿主机上查别在容器里傻找。5. 实战一次端口被占引发的完整排查链路理论讲了这么多必须来一场完整的实战演练。场景很常见部署一个新服务启动时报Address already in use端口8080。下面是我会走的完整排查链路每一步都讲为什么。5.1 第一步先看谁占了端口不要盲目重启第一反应不是把服务再起一次而是先看端口的状态ss -tlnp | grep 8080输出LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:((java,pid23456,fd21))好了占用者是PID 23456的一个Java进程。但这还不够不能因为这个PID存在就直接kill -9。5.2 第二步用ps确认这个进程能不能动ps -fp 23456输出UID PID PPID C STIME TTY TIME CMD app 23456 1 0 3月12 ? 00:10:22 java -jar old-app.jar --server.port8080看到PPID1且STIME是3月12日说明这是个已经运行很久的Java进程很可能是上次发版时没有停干净的旧版本服务。这时候有两种处理思路如果它就是旧版服务且确认要部署新版顶替它用服务管理工具正常停止不要直接kill -9。如果它根本不是自己负责的服务先查一下它是哪个应用、哪个团队在用的避免误杀。这一步是整个排查里最容易省掉、也最容易出事的环节。我见过太多人查完ss就kill -9 23456结果那是隔壁团队的数据库管控进程。判断进程能不能动ps -fp给出的PPID、启动时间、完整命令行就是三个最直接的依据。5.3 第三步按顺序停进程而不是上来就kill如果确认要清理这个进程正确的顺序是先找有没有系统d服务管理systemctl status 23456或systemctl list-units | grep old-app能走systemctl就走systemctl stop。没有服务管理就发SIGTERM让进程自己收尾kill 23456。过几秒检查是否退出ps -fp 23456。还没退出再用SIGKILLkill -9 23456。为什么这个顺序很重要SIGTERM允许进程做清理工作——关闭数据库连接、写结束日志、释放锁SIGKILL则直接告诉内核“就地正法”进程无法做任何善后。对Java进程来说被kill -9还可能影响某些有状态数据的持久化甚至日志文件处于写一半的状态。5.4 第四步进程杀完端口还报“被占”考虑TIME_WAIT这是最常见的诡异情况用ss -tlnp | grep 8080已经查不到监听进程了但服务启动依然报Address already in use。这时候要看的是另一个输出ss -tan | grep 8080如果看到大量TIME-WAIT状态的连接比如TIME-WAIT 0 0 1.2.3.4:8080 1.2.3.4:56789原因就清楚了虽然监听进程已退出但系统里仍有大量旧连接处于TCP的TIME_WAIT关闭状态端口会在约2MSL时间内继续被占用。这种情况通常不影响正常使用——因为绝大多数服务端框架默认设置了SO_REUSEADDR允许新进程在TIME_WAIT状态下重新绑定端口。如果你服务的起停代码是手写socket且没开这个选项就会卡住。解决办法两个在代码里加上SO_REUSEADDR这是根治方案临时绕过的话换个端口启动或等待两分钟再启动。值得单独说一句看到TIME-WAIT不要恐慌地去改tcp_fin_timeout之类的内核参数。除非这是长期、大规模、影响业务的场景否则绝大多数情况下它是正常关闭机制的副产品。5.5 第五步验证链路真的通了进程清理完、新服务起来后不要急着宣布胜利做一套闭环验证# 确认新进程监听成功 ss -tlnp | grep 8080 # 本机自测 curl -v http://127.0.0.1:8080/health # 确认进程PID正确 ps -fp $(pgrep -f new-app.jar)这三步走完才算真正排查结束。整个链路回顾起来就是ss找到端口占用者 →ps判断进程身份与状态 → 决定怎么处理 → 杀完检查TIME_WAIT→ 重启后验证。三把剑在这里各自出场一次缺一不可。6. 端口通不通telnet、nc、纯bash脚本三种姿势6.1 telnet最直观、但也最容易被环境限制判断一个端口通不通最经典的姿势是telnettelnet 192.168.1.50 8080连上了会显示Connected to 192.168.1.50被拒绝则提示Connection refused对方没响应则长时间卡在那。问题在于很多系统默认不装telnet客户端Windows 7/10也默认没启用这个功能。Windows这边在“启用或关闭Windows功能”里勾上“Telnet客户端”重启后即可用。我见过不少同事在这一步被卡住所以顺带提一句。但说实话如果让我在日常运维里选我更推荐nc。6.2 nc更现代的端口探测姿势ncnetcat几乎是我探测端口的第一选择了。最常用的探测姿势nc -zv 192.168.1.50 8080-z只扫描不发送数据专门测试连通性-v显示详细信息还可以加-w 3设置超时时间避免在不可达IP上干等输出Connection to 192.168.1.50 8080 port [tcp/*] succeeded!就是通了。nc还经常用来看某个端口返回的协议初始内容比如echo | nc -w 2 192.168.1.50 3306能看到MySQL的版本握手信息这在判断“端口通但协议不对”时非常有用。6.3 纯bash的/dev/tcp探测能少装一个依赖就少装一个在某些最小化安装的服务器上nc也没装telnet更不可能有但你又必须测端口。其实bash自己就内置了/dev/tcp这个虚拟设备timeout 3 bash -c cat /dev/null /dev/tcp/192.168.1.50/8080 echo port open || echo port closed/timeout原理bash会把/dev/tcp/host/port当作一个可读写的网络socketcat /dev/null往里传空数据并等待连接建立。能建立就说明端口通。用timeout 3限制最长等待3秒避免卡死。批量测多个端口可以写个简单循环for p in 22 80 443 3306 8080; do timeout 2 bash -c cat /dev/null /dev/tcp/192.168.1.50/$p 2/dev/null \ echo port $p: open \ || echo port $p: closed/filtered done这段脚本在纯bash环境里就能跑是出差到陌生环境时最轻量的探路工具。6.4 端口不通时别再重复测了先分清是哪一类问题端口不通有至少三种截然不同的原因用同一个命令测N遍也没有意义。先做一个判断测试结果可能原因下一步操作立即Connection refused目标主机可达但端口上没有服务监听上目标机查ss -tlnp看服务是否真的起来了、是否监听在错误地址长时间超时目标主机不可达或中间防火墙丢弃了包用ping和traceroute判断网络路径检查防火墙规则telnet能进但随后被断开或打出乱码端口有服务但协议不匹配或服务异常检查是不是端口对应了别的协议看服务日志可能是TLS握手失败等最典型的一个误判案例服务监听在127.0.0.1:8080你拿另一台机器去测192.168.1.50:8080结果永远Connection refused。这不是网络问题是服务根本没监听对外网卡。先回到第二剑的ss -tlnp看一眼监听地址是不是0.0.0.0这类问题十秒就能定位。7. 冷门但救命的细节进程名、容器端口、资源极限和面试真题7.1 进程名匹配的坑截断、重名、pgrep的局限性前面提到过Linux进程名15字符截断的问题。这里再补充一个实际教训用ps -C匹配进程名时-C匹配的是进程名comm不是完整命令行。而我们的Java进程往往是通过java -jar app.jar启动的comm显示的是java不是app.jar。所以ps -C app.jar什么都匹配不到很正常。更可靠的是按完整命令行匹配pgrep -af app.jar如果重名进程很多还有pidof和pgrep -o、pgrep -n可以取最老或最新的实例。这些看似小细节在自动化脚本里就是致命的bug本意是kill旧服务结果pkill java把所有Java进程全杀了——这种事故我至少在事故报告里见过三次。7.2 前台进程、孤儿进程与正确后台运行热搜里“监控前台进程”背后有一个很实在的问题服务在终端前台跑着一关终端进程就没了或者SSH断掉服务就被杀了。这里的关键词是会话和终端信号。普通情况下进程属于当前shell的会话终端关闭时会收到挂断信号SIGHUP并退出。解决办法三个选项nohup ./server ./server disown setsid ./servernohup忽略SIGHUP但进程还是当前会话的成员disown把作业从shell的作业表里移除让它不怕shell退出setsid直接让进程成为新会话的领导者和当前终端彻底脱离。现代Linux服务器上最正经的做法是写systemd unit用systemd托管这样它开机自启、崩溃拉起都有系统保障。如果只是临时跑个东西nohup或setsid足够但千万别再养成“开个screen/ssh挂着”的习惯。7.3 WSL、容器和端口映射别在本机端口里找不存在的进程随着WSL和容器环境越来越普及“端口看着被占但进程查不到”的情况也越来越多。原因通常是你看到的端口映射并不在当前这个命名空间里。WSL场景WSL里启动的服务通常可以通过localhost从Windows侧访问因为WSL2默认做了端口转发但反过来在WSL里查宿主机Windows的端口映射是看不到的。排查这种问题思路是分清两侧的命名空间。容器场景宿主机上ss -tlnp显示0.0.0.0:8080被某个Docker-proxy进程占用但ps里看不到业务进程本身因为业务进程在容器内部命名空间。这时候要docker ps和docker exec进容器查别拿着宿主机ps结果一脸困惑。这些环境用“三剑客”时边界感很重要——先确认你自己在哪一层再判断该用哪一层的工具。7.4 系统资源极限句柄数、进程数、Too many open files端口和进程排查到后期经常撞上资源限制。最典型的是Too many open files。这里我想讲清楚一个最常见的误区ulimit -n限制的是“一个进程能打开的文件描述符数量”不是系统全局的。所以服务报Too many open files时先看这个进程的fd数ls /proc/PID/fd | wc -l如果这个数接近ulimit -n的软限说明该调进程的fd限制或者排查是不是有fd泄漏。我之前处理过一个网关进程fd数一路涨到5万查lsof -p PID发现全是CLOSE_WAIT的socket——不是系统配置不够是代码里连接没关闭。另外进程数的限制也值得留意。如果大量线程或孤儿进程堆积系统pid_max也可能成为瓶颈。这类问题初期症状很隐蔽服务随机失败、端口连不上、进程起不来最后用dmesg -T | tail一看全是fork: Cannot allocate memory之类的字眼。排查这类问题ss -s和ps -eLf | wc -l能快速估算系统整体连接数和线程数。7.5 面试常考的这些概念和现实排查怎么对应热搜词里有“进程通信IPC”“进程等待wait”“线程与进程”这些概念在面试里高频出现但你要是只背概念不联系实际排查面试官一问到场景就露馅。我通常这样理解进程等待wait对应僵尸进程的产生和回收排查僵尸进程时就是现学现用。进程通信IPC包括管道、信号量、共享内存、socket看lsof -p PID时那些FIFO、事件fd、socket就是这些IPC的具体形态。进程调度算法对应ps里的STAT状态、pri、ni优先级列也对应D状态进程为什么拿到信号也无法退出。说白了考点从来不是靠背是你在真实排查里遇到过、处理过跟面试官聊的时候能自然讲出“当时我看到STATD的状态就知道这不是应用能解决的要去查底层I/O”。这种回答和背概念的回答高下立判。8. 我日常的“三剑客”使用清单与最后的小建议文章到这里核心内容已经讲完。最后不用做什么大总结我只分享一些自己长期保留下来的使用习惯和踩出来的经验。我习惯在每个新环境先确认命令可用然后把这几个别名写进~/.bashrcalias portss -tlnp alias prockps -eo pid,ppid,user,stat,lstart,time,cmd alias whoisportlsof -i alias cportlsof -Pn -ilsof -Pn这个细节值得单独提一下-P禁用端口号转服务名-n禁用IP转主机名。不加这两个参数时lsof可能因为反向DNS解析卡住在DNS异常环境里让人抓狂。一加秒出结果。我最后想说的建议是这些命令的威力不在单个命令背得多熟而在遇到问题时的组合节奏。服务起不来先看监听、确认进程身份后再决定停不停、杀完要看时序状态、通不通要分层测。这套节奏只有在真实事故里练过才会有肌肉记忆平时业务平稳时找个测试环境故意模拟几次“端口被占”“进程假死”把这套链路从头走一遍比刷十篇教程都管用。三剑客听着高大上底层逻辑其实就是先看清再动手最后验证。运维这份工作火眼金睛靠的不是玄学是每一步都有依据。
返回列表