3. Linux 端口排查:ss vs netstat、lsof、fuser 与 /proc

端口排查,说白了就是回答三个问题:谁在监听?谁在用?怎么把它赶走? 我干了十几年运维,每次遇到“端口被占用”的报错,脑子里蹦出来的就是这套组合拳。今天咱们就把这几个工具掰开揉碎了讲清楚。

核心思路:先查监听状态 → 再定位进程 → 最后决定是保留还是释放。别一上来就 kill -9,容易误伤。

Linux 端口排查工具链 端口被占用? ss / netstat lsof fuser 查看监听 & 连接状态 ss -tlnp / netstat -tlnp ss 更快,netstat 更全 定位进程 & 文件描述符 lsof -i :端口号 lsof -i -P -n | grep LISTEN 强制释放端口 fuser -k 端口/tcp 慎用!会直接 kill 进程 /proc 文件系统:终极调试 cat /proc/PID/net/tcp | awk '{print $2}'

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 负责“刨根问底”。下次遇到端口问题,按这个顺序来,基本都能搞定。