Linux 查看端口占用进程:lsof / ss / netstat 等价写法对比
本文要回答的问题
- Linux 上怎样查看某个端口(如 8080、443)被哪个进程占用?
ss、lsof、netstat三条命令查端口时分别怎么写,输出怎么看?- 怎么区分端口是在监听(LISTEN)还是已有连接(ESTABLISHED)?TCP 和 UDP 查法有何不同?
- 反过来,给定一个进程/PID,怎么查它用了哪些端口?
- 查到占用端口的 PID 之后怎么结束进程?容器里为什么常常看不到进程名?
这是一篇「端口与进程互相定位」的收口文。三条命令各自的完整手册见 ss、lsof、netstat,本篇只讲「查端口被谁占用」这一件事的等价写法与取舍。
一、三条命令,同一个问题
「我想知道 8080 端口是谁在听」这句话,三条命令各有一行答案:
lsof -i :8080 # 最直接:按端口号查,输出进程名/PID/用户
ss -tuanp | grep :8080 # 现代首选:TCP+UDP 全部套接字 + 进程
netstat -tunlp | grep :8080 # 传统写法:老系统和容器里常见注意端口号前面的冒号不能省:写成 grep 8080 会把 PID 为 8080、或地址里含 8080 的无关行也匹配出来;:8080 才精确匹配端口字段。
三条命令横向对比如下,选型时先看环境里有哪条、再看要不要 root。
| 维度 | lsof | ss | netstat |
|---|---|---|---|
| 典型查端口命令 | lsof -i :8080 | ss -tuanp | grep :8080 | netstat -tunlp | grep :8080 |
| 是否需要 root | 普通用户能看自己的进程;看全系统建议 root | 显示 -p 进程列建议 root,否则只显示自己的 | 显示 -p 进程列建议 root |
| 进程信息位置 | 独立的 COMMAND/PID/USER 列,最易读 | 行尾 users:(("nginx",pid=812,fd=6)) | 行尾 812/nginx |
| 数据来源 | 遍历 /proc + /proc/net | 内核 netlink 套接字接口 | 逐行解析 /proc/net/tcp 等 |
| 连接数多时 | 一般 | 最快,连接上万也不卡 | 慢,已被标记废弃 |
| 默认是否只看监听 | 否,监听和已建立连接都列 | 否(要只看监听加 -l) | -l 时只看监听 |
| 适用场景 | 「某端口被谁占」一问一答 | 日常排查、连接状态分析 | 老发行版、精简容器、肌肉记忆 |
| 容器内可用性 | 镜像通常不带,需自行安装 | 随 iproute2,多数基础镜像有 | 精简镜像反而常保留 net-tools |
结论先行:交互式登录一台正常的现代 Linux,查端口占用优先用 ss;只想用最短命令回答「这端口谁占的」,用 lsof;进了老系统或容器发现前两条都不存在,netstat 兜底。
二、选命令的决策路径
下图把上面的取舍画成一条排查路径:先确认端口类型(TCP/UDP、只看监听还是连已建立连接一起看),再按环境选择命令,拿不到进程名时补 root,最后才考虑进程在别的网络命名空间(容器)里。

这张图有两个实际踩点。其一,普通用户执行带 -p 的 ss/netstat 时,别人进程的套接字照样列出来,但进程列是空的——不是没占用,是权限不够看,sudo 一下 PID 就出现了。其二,在容器宿主上 grep :8080 可能只看到 docker/containerd-shim 甚至什么都没有,因为监听套接字属于容器内进程的网络命名空间;进入容器再查,或用 nsenter -t <容器进程PID> -n ss -tlnp 进入对应命名空间查,才能看到真实归属。
三、ss:现代主力写法
ss 是 socket statistics 的缩写,直接走内核 netlink 接口,查端口和查连接状态都快。字母含义先记牢:t=TCP、u=UDP、l=只看监听、a=全部(含监听与已建立)、n=数字显示不做反解(快且避免 DNS 卡顿)、p=显示进程。
ss -tlnp # 所有 TCP 监听端口 + 进程
ss -tunlp # TCP+UDP 监听端口 + 进程
ss -tuanp # TCP+UDP 全部套接字 + 进程(含 ESTABLISHED)
ss -tlnp | grep :8080 # 谁在监听 8080
ss -tanp | grep :443 # 443 上的监听与已建立连接搜索词里常出现的 ss -tuanp 就是「TCP+UDP、全部状态、数字形式、带进程」的总览组合,相当于把整张套接字表一次拉出来再 grep。输出长这样:
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
tcp LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=5678,fd=42))
tcp ESTAB 0 0 10.0.0.2:443 203.0.113.9:51122 users:(("nginx",pid=812,fd=12))进程信息在行尾的 users:(...) 里,格式是 进程名,pid=PID,fd=文件描述符。Local Address:Port 为 0.0.0.0:8080 表示监听本机所有 IPv4 地址,127.0.0.1:8080 则只对本机开放——后者从外部连不上是正常的,常被误判为服务没起。
顺带认识两列排查时很有用的字段:Recv-Q 和 Send-Q。在 LISTEN 行上,Recv-Q 是全连接队列当前长度、Send-Q 是队列上限(backlog),高并发下若当前值顶到上限,新到的 SYN 会被内核丢弃,表现为客户端偶发连不上;在 ESTABLISHED 行上,Recv-Q 持续大于零意味着数据已经到内核、应用却迟迟没 read,常见于 Java 应用 GC 停顿或单线程处理不过来。查端口归属时扫一眼这两列,常常能把「端口冲突」问题直接升级成「服务异常」问题,少走弯路。
ss 常用过滤选项
| 选项/写法 | 作用 | 查端口时的用途 |
|---|---|---|
-t / -u | 只看 TCP / UDP | 区分协议 |
-l / -a | 只看监听 / 全部状态 | 查服务用 -l,查连接用 -a |
-n | 数字显示,不解析服务名和主机名 | 必加,防止卡在 DNS |
-p | 显示进程名与 PID | 定位归属,建议配合 sudo |
state established | 按 TCP 状态过滤 | ss -tanp state established |
( dport = :3306 or sport = :3306 ) | 按端口表达式过滤 | grep 之外的精确写法 |
-4 / -6 | 只看 IPv4 / IPv6 | 双栈环境下消歧 |
四、lsof:一问一答最省事
lsof 的本意是 list open files,而套接字在 Linux 上也是文件,所以它能直接回答「谁打开了这个端口」。查端口的核心参数是 -i:
lsof -i :8080 # 8080 端口上的所有套接字(监听+连接,TCP+UDP)
lsof -iTCP:8080 -sTCP:LISTEN # 只看谁在 TCP 8080 上监听
lsof -iUDP:53 # UDP 53
lsof -nP -i :443 # -n 不解析主机名、-P 不解析端口名,输出更快更直观输出字段天然就是为「找进程」设计的:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 5678 app 42u IPv6 921830 0t0 TCP *:webcache (LISTEN)
nginx 812 root 12u IPv4 882134 0t0 TCP 10.0.0.2:https->203.0.113.9:51122 (ESTABLISHED)COMMAND/PID/USER 三列直接给出进程名、PID、属主,NAME 列末尾括号里是状态。习惯上加上 -nP:否则 lsof 会把 8080 反解成 webcache、443 反解成 https 这类 /etc/services 里的服务名,反而对不上你要查的数字端口。要查看启动的服务及端口信息,sudo lsof -nP -iTCP -sTCP:LISTEN 一把列出所有监听者,按 USER 列还能看出服务是不是被 root 直接拉起。
FD 列也值得看一眼:值是数字加字母,比如 42u 表示 42 号文件描述符、以读写方式打开。同一个进程在同一端口上常常出现多行——master 进程持有 LISTEN,worker 进程也继承了这个监听套接字,nginx、Apache 都是这种模型,所以「443 被一堆 PID 占着」并不是异常。反查端口时如果看到一个不认识的进程名,可以立刻用 PID 跳到 ps 命令核实:ps -p <PID> -o pid,ppid,lstart,cmd,父进程和启动时间能帮你判断它是系统服务、手工拉起的测试程序,还是被入侵后留下的反弹连接——后者通常表现为一个陌生进程对外建立 ESTABLISHED 连接,而非监听端口。
五、netstat:传统与容器内兜底
netstat 来自 net-tools 包,新发行版上已建议用 ss 替代,精简镜像和老系统里却仍然常见。字母含义与 ss 几乎一致:
netstat -tunlp # TCP+UDP 监听端口 + 进程
netstat -tunlp | grep :8086 # 查看 8086 被谁占用
netstat -tanp | grep :443 # 443 的监听与 ESTABLISHED 连接
netstat -tuanp # TCP+UDP 全部状态(等价 ss -tuanp)要看 netstat 端口状态 established,就去掉 -l 加上状态过滤:
netstat -tanp | grep ESTABLISHED | grep :443netstat 的进程列在地址之后,形如 812/nginx,PID 在前、进程名在后。看不到进程列先检查两件事:有没有带 -p、是不是 root;netstat 的 -p 对非 root 同样只显示自己的进程。
新系统上如果敲 netstat 提示 command not found,Debian/Ubuntu 系用 apt install net-tools、RHEL 系用 yum install net-tools 即可补回;但既然要现装,不如直接装 iproute2 用 ss。netstat 还有两个查端口时顺手可用的参数:-c 让输出每秒连续刷新,观察连接建立和断开的动态;-w 加入 raw 套接字,排查一些非常规监听时才用得到。需要注意的是,连接数很大时 netstat 逐行解析 /proc/net/tcp 会明显卡顿,线上高峰期慎用不带过滤的全量输出,最好先 grep 端口缩小范围。
六、LISTEN 与 ESTABLISHED 是两件事
查端口时最常见的混淆,是把「服务在监听」和「客户端已连上」当成一回事。一个 443 端口上通常同时存在一条 LISTEN 和若干条 ESTABLISHED:前者属于 nginx,后者是 nginx 与各个客户端之间的连接。
| 对比项 | LISTEN | ESTABLISHED |
|---|---|---|
| 含义 | 套接字处于监听态,等待新连接 | 三次握手完成,连接已建立 |
| 对端地址 | 0.0.0.0:* / *:*(无对端) | 具体的客户端 IP:端口 |
| 谁拥有 | 服务端进程 | 服务端和客户端各持有一条 |
| ss 查法 | ss -tlnp | grep :443 | ss -tanp state established | grep :443 |
| netstat 查法 | netstat -tlnp | grep :443 | netstat -tanp | grep :443 |
| 回答什么问题 | 服务起没起、端口冲突 | 当前有谁连着、连接数多少 |
TCP 与 UDP 也要分清。UDP 无连接,没有 ESTABLISHED 状态,ss -tlnp(只带 t)永远查不到 DNS 的 53 端口;查 UDP 必须带 u,即 ss -ulnp 或一把梭的 ss -tunlp/ss -tuanp。UDP 套接字的输出也没有对端地址,看到的是 *:*,排查「UDP 端口被谁占用」时直接认 LISTEN(UNCONN)那一行的进程即可。另外,UDP 允许不同进程分别绑定同一端口的不同地址,输出里可能出现同一端口的多行,认地址列区分,不要误以为命令出错。
还有一个高频误判:服务启动报「address already in use」,但 ss -tlnp 又看不到监听者。这时把 -l 去掉查全部状态,往往能发现端口上还挂着 TIME-WAIT 连接——上一个进程虽已退出,端口处于 2MSL 等待期。这不算进程占用,稍等即释放;程序侧对监听套接字开启 SO_REUSEADDR 即可立即重启,这也是几乎所有服务器框架的默认行为。
七、端口与进程双向定位全景
实际排查往往是双向走的:从端口出发找到 PID,或者从一个可疑进程出发看它开了哪些端口、连了哪些对端。下图把两个方向的入口命令汇总到一张图上,任意一侧拿到线索都能跳到另一侧核实。

读图要点是中间的 PID 节点:它是两个方向的交汇点。端口侧查出来的 PID 要用 ps -p <PID> -o pid,ppid,cmd 核实是不是预期服务;进程侧反查出来的端口列表,也应能在 ss -tlnp 全量输出里再次对上。只有一侧对得上、另一侧对不上时,多半是命名空间或权限问题,而不是命令出错。
八、常见疑问速查
| 现象/疑问 | 原因与处理 |
|---|---|
ss -p 进程列是空的 | 套接字属于其他用户的进程,普通用户无权看归属,sudo 重跑 |
| grep 8080 匹配出一堆无关行 | 没加冒号,写成 grep :8080 精确匹配端口字段 |
| 查不到 UDP 的 53 端口 | 只带了 -t,UDP 要加 -u:ss -ulnp 或 ss -tunlp |
端口显示成 https、webcache | 没加 -n(ss/netstat)或 -P(lsof),被反解成 /etc/services 服务名 |
| 服务明明起着,外部连不上 | 监听地址是 127.0.0.1 而非 0.0.0.0,只对本机开放;再查防火墙/安全组 |
:::8080 是什么 | IPv6 的通配监听,多数情况下双栈同时接受 IPv4 连接 |
| kill 掉进程端口马上又出现 | 进程被 systemd/supervisor 守护重启,应 systemctl stop 对应服务 |
| 宿主上只看到 docker-proxy | 真实监听在容器 netns 内,进容器查或用 nsenter -t <PID> -n |
| TIME-WAIT 的端口算不算占用 | 不算监听占用,连接已关闭只是在等 2MSL,新服务通常能正常 bind(除非地址未开复用) |
| 一条命令都没有的极简镜像 | 直接读 cat /proc/net/tcp、cat /proc/net/udp,十六进制端口需换算 |
九、可运行 demo
demo 1:查 8080 被谁占用,拿到 PID
sudo ss -tlnp | grep :8080
# LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=5678,fd=42))等价写法:sudo lsof -nP -iTCP:8080 -sTCP:LISTEN。查具体端口号(如查看 443 占用、查看 8086 占用)时只需替换数字,套路完全一样。
demo 2:确认 PID 后结束进程
pid=$(sudo ss -tlnp | grep :8080 | grep -oP 'pid=\K[0-9]+' | head -1)
echo "$pid" # 先看清楚是谁,再决定动不动手
kill "$pid" # 优雅终止
# kill "$pid" 后端口仍在,再考虑:
sudo kill -9 "$pid"kill 之前务必确认 PID 对应的服务(ps -p "$pid" -o pid,cmd),端口可能是被 systemd 管理的正式服务,直接 kill 会被自动重启,正确做法是 systemctl stop <服务名>。上面用 grep -oP 'pid=\K[0-9]+' 直接从 ss 行尾抠 PID,适合脚本化;手工操作时更稳妥的方式是肉眼读 users:(...) 里的 PID,避免一行被多个进程共享时抠错。另外 Java 类进程名字往往都是 java,光看进程名分不清是哪个服务,用 jps -l 或读它的工作目录能进一步确认身份。
demo 3:按进程/PID 反查它使用的端口
查看进程使用端口,方向反过来,在带 -p 的全量输出里 grep 进程名或 PID:
sudo ss -tuanp | grep nginx # nginx 监听和建立的所有套接字
sudo lsof -nP -p 812 -i # PID 812 打开的所有网络端口
sudo netstat -tuanp | grep 812/ # netstat 写法demo 4:列出本机所有监听服务
sudo ss -tunlp # linux 查询网络端口总览
sudo lsof -nP -iTCP -sTCP:LISTEN # 只列 TCP 监听,带属主
systemctl list-units --type=service --state=running # 服务名维度对照demo 5:数某端口上的活动连接
ss -tan state established '( sport = :443 )' | grep -v State | wc -l十、组合技与容器坑
排查端口冲突的一条完整链路:ss -tlnp | grep :8080 找到旧进程 PID,ps -p <PID> -o pid,ppid,cmd 看它是谁拉起的,再决定 kill 还是 systemctl stop;怀疑连接泄漏时去掉 -l 改看全部状态,并留意 CLOSE-WAIT 堆积,状态语义见 ss 第五节。
容器环境有三个固定坑:第一,宿主上看到的是端口映射(docker-proxy 或 docker 转发),真实监听进程在容器内网络命名空间,需 docker exec <容器> ss -tlnp 进容器查;第二,精简镜像里 ss、lsof、netstat 可能一个都没有,可用 cat /proc/net/tcp 临时顶,或装 iproute2;第三,进程以非 root 运行时 -p 列缺失,进容器后记得提权。
这一篇在知识体系里的位置
- ss:本篇推荐的主力命令,连接状态、队列积压、TIME-WAIT/CLOSE-WAIT 排查的完整手册。
- lsof:查「某端口被谁占用」最直接的命令,也是查文件被谁占用、fd 泄漏的工具。
- netstat:传统写法,老系统与容器内仍在用,路由表、接口统计也靠它。
总结
查端口被谁占用,记住三行等价写法:lsof -nP -i :8080 最直白、sudo ss -tlnp | grep :8080 是现代首选(TCP+UDP 全量用 ss -tuanp)、netstat -tunlp | grep :8080 给老系统和容器兜底;LISTEN 回答「谁在监听、端口冲突」,ESTABLISHED 回答「谁正连着」,查 UDP 必须带 u;进程列空白先 sudo,宿主上查不到进容器或换 netns,kill 之前先 ps 确认 PID 归属。