3. Linux 端口排查:ss vs netstat、lsof、fuser 与 /proc
端口排查,说白了就是回答三个问题:谁在监听?谁在用?怎么把它赶走? 我干了十几年运维,每次遇到“端口被占用”的报错,脑子里蹦出来的就是这套组合拳。今天咱们就把这几个工具掰开揉碎了讲清楚。
核心思路:先查监听状态 → 再定位进程 → 最后决定是保留还是释放。别一上来就 kill -9,容易误伤。
3.1 ss vs netstat:谁才是现代 Linux 的标配?
先说说这两个老冤家。netstat 是经典工具,几乎每个 Linux 老手都用过。但在我现在管理的 CentOS 7/8 和 Ubuntu 20.04+ 环境里,ss 才是默认安装的那个。为什么?因为 netstat 依赖 /proc/net 解析,而 ss 直接读内核的 socket 哈希表,速度不是一个量级。
我举个例子。有一次线上告警,说某个 Java 应用端口连不上。我 ssh 上去敲 netstat -tlnp,等了快 3 秒才出结果。换成 ss -tlnp,0.3 秒就出来了。在连接数上万的生产环境,这个差距就是能不能快速定位问题的关键。
| 对比项 | ss | netstat |
|---|---|---|
| 数据来源 | 内核 socket 哈希表 | /proc/net/tcp 等文件 |
| 速度(万级连接) | 毫秒级 | 秒级 |
| 默认安装 | iproute2 包,通常预装 | net-tools 包,需手动安装 |
| 常用参数 | -tlnp / -tunap | -tlnp / -tunap |
| 输出可读性 | 稍显紧凑 | 更直观 |
我的习惯:日常排查用 ss -tlnp,需要看完整连接状态(比如 TIME_WAIT 数量)时用 ss -tunap。只有老系统或同事习惯 netstat 时,我才用 netstat。
来看看实际用法。查 8080 端口谁在监听:
# 用 ss 查
ss -tlnp | grep 8080
# 用 netstat 查
netstat -tlnp | grep 8080
输出结果大同小异,但 ss 会多显示一些内核信息。我个人更推荐 ss,因为它在高并发场景下不会成为瓶颈。
3.2 lsof:定位端口背后的进程
ss 和 netstat 能告诉你端口在监听,但有时候你需要更细的信息——比如这个进程打开了哪些文件?它的工作目录是什么?这时候 lsof 就派上用场了。
我记得有一次排查 Nginx 端口冲突,ss 显示 80 端口被一个 PID 为 1234 的进程占用,但进程名显示的是 nginx。用 lsof 一看,发现其实是另一个老版本的 Nginx 进程没完全退出,还在占着端口。lsof 把进程的完整命令行、打开的文件描述符都列出来了,一目了然。
# 查 3306 端口谁在用
lsof -i :3306
# 只看监听状态
lsof -i -P -n | grep LISTEN
# 查某个 PID 打开了哪些端口
lsof -i -P -n -p 1234
注意:lsof 在连接数特别多的时候会有点慢,因为它要遍历所有进程的文件描述符。我一般先用 ss 缩小范围,再用 lsof 做精确确认。
lsof 的输出里,COMMAND 是进程名,PID 是进程号,NAME 列会显示 IP:端口 或 *:端口。如果看到 *:8080 (LISTEN),说明这个端口在所有网卡上都监听。
3.3 fuser:强制释放端口,但别乱用
有时候你确定某个端口被僵尸进程占着,或者某个服务挂了但端口没释放。这时候 fuser 可以帮你直接干掉它。
我曾经在客户现场遇到过一个问题:Tomcat 正常关闭后,8080 端口还处于 TIME_WAIT 状态,重启 Tomcat 时报错端口被占用。客户急得不行,我上去敲了一行:
fuser -k 8080/tcp
端口立刻释放,Tomcat 顺利启动。但注意,fuser -k 会直接给占用端口的进程发 SIGKILL 信号,也就是 kill -9。如果那个进程是数据库或者关键业务,后果很严重。
避坑指南:我曾经在生产环境上误操作,用 fuser -k 释放了一个 Redis 端口,结果那个 Redis 实例是主库,直接导致缓存雪崩。从那以后,我每次用 fuser 之前都会先用 lsof 确认一下进程是什么。
fuser 的其他常用参数:
-v:显示详细信息,包括进程 PID 和用户-n tcp:指定协议类型,比如fuser -v -n tcp 8080-i:交互模式,kill 之前会问你是否确认
# 安全做法:先查看,再决定是否 kill
fuser -v 8080/tcp
# 确认无误后,再强制释放
fuser -k 8080/tcp
3.4 /proc 文件系统:终极调试手段
如果上面这些工具都不好使,或者你想从内核层面理解端口到底是怎么被管理的,那就得看 /proc 文件系统了。说白了,/proc 是内核暴露给用户态的一个接口,里面全是文本文件,你可以用 cat、awk、grep 直接读。
我记得有一次,ss 和 netstat 都显示某个端口处于 CLOSE_WAIT 状态,但 lsof 找不到对应的进程。后来我直接去 /proc/net/tcp 里查,发现那个 socket 的 inode 对应的进程已经退出了,但内核还没来得及清理。这种边缘情况,用常规工具根本查不出来。
# 查看所有 TCP 连接的内核信息
cat /proc/net/tcp
# 只看监听状态(0A 表示 LISTEN)
cat /proc/net/tcp | awk '$4 == "0A" {print $2, $10}'
# 根据本地端口过滤(端口号是十六进制)
cat /proc/net/tcp | grep ":1F90" # 8080 的十六进制是 1F90
/proc/net/tcp 的每一列含义:
| 列 | 含义 | 示例 |
|---|---|---|
| sl | socket 序号 | 0 |
| local_address | 本地 IP:端口(十六进制) | 00000000:1F90 |
| rem_address | 远端 IP:端口(十六进制) | 00000000:0000 |
| st | 连接状态(0A=LISTEN, 01=ESTABLISHED) | 0A |
| uid | 用户 ID | 0 |
| inode | socket 的 inode 号 | 123456 |
小技巧:如果你在 /proc/net/tcp 里看到一个 socket 的 inode,但 lsof -i 找不到对应进程,可以试试 find /proc -name "fd" -exec ls -l {} \; 来全局搜索这个 inode。虽然慢,但有时候能救命。
嗯,到这里,Linux 端口排查的四个核心工具就讲完了。ss 和 netstat 负责“看”,lsof 负责“查”,fuser 负责“杀”,/proc 负责“刨根问底”。下次遇到端口问题,按这个顺序来,基本都能搞定。