先交代一个背景:刚接手一台Linux服务器,兴冲冲部署完新服务,结果服务进程起不来,日志里只留下一句“Address already in use”,第一反应是摸出netstat -tlnp看一眼是哪个进程占了端口,这一套操作几乎每个运维都干过,但真到了排查复杂占用,或者遇到容器、双网卡、TIME_WAIT这些边界情况时,光靠一条netstat往往不够,很多新手正是在这里被卡住。
这篇就把“Linux查看端口被占用”这件事彻底讲透,从最常用的三条命令,到反查进程、处理占用、批量检测、跨主机探测,再到实际踩坑案例,全部过一遍。内容覆盖netstat、ss、lsof、fuser、lsof -i、/proc文件系统、strace这些核心工具,适合刚接触Linux的运维新人,也适合偶尔需要上服务器排查问题的开发、测试、数据分析师。
1. 端口被占用的第一反应:netstat、ss、lsof三件套
新手查端口占用,记忆里通常只有三件套:netstat、ss、lsof。这三条命令各有侧重,也有各自的适用场景,下面逐个说,连同它们最容易出问题的地方一起讲。
1.1 netstat:经典但稍显过时的选择
netstat -tlnp 是流传最广的一条命令,功能是列出所有处于监听状态的TCP端口,并显示对应的进程PID和进程名。
bash复制netstat -tlnp
输出大概长这样:
bash复制Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1234/sshd
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 2345/mysqld
tcp6 0 0 :::8080 :::* LISTEN 3456/java
看到Local Address这一列,0.0.0.0:22表示所有IPv4地址上的22端口都被监听,127.0.0.1:3306表示只监听本机回环地址,外部无法通过局域网访问,:::8080则是IPv6的任意地址监听。PID/Program name就是进程信息,如果看不到,需要换成root用户执行。
netstat确实老了,但它足够普及,几乎每台发行版默认自带。遇到没有ss的老系统,或者不巧系统里装了精简版工具链,netstat依然能撑住场面。缺点是高并发环境下输出慢,大量连接时CPU占用偏高,而且部分发行版已经默认不安装net-tools包,需要额外apt install net-tools或yum install net-tools。
1.2 ss:现代系统的首选
ss 是iproute2包提供的工具,全称socket statistics,设计目标就是替代netstat。它直接读取内核的socket信息,不依赖解析/proc/net,所以速度更快,输出也更直观。
bash复制ss -tlnp
命令拆解:
-t:只显示TCP socket。-l:只显示监听的socket。-n:不做域名和端口名解析,直接显示IP和数字端口,解析会拖慢速度。-p:显示进程信息,需要root权限。
输出示例:
bash复制State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
LISTEN 0 100 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=2345,fd=19))
注意Process列,格式是users:(("进程名",pid=进程号,fd=文件描述符)),如果进程名带空格或特殊字符,这一列会变得比较难读。实际排查中,我一般直接ss -tlnp | grep :8080快速定位,或者用ss -tlnp sport = :8080这种更精准的过滤方式。
1.3 lsof:按端口查进程的利器
lsof 全称list open files,在Linux里一切皆文件,网络连接也是一种文件描述符,所以lsof -i :端口号可以直接查占用该端口的进程。
bash复制lsof -i :8080
输出示例:
bash复制COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 3456 root 46u IPv6 123456 0t0 TCP *:8080 (LISTEN)
java 3456 root 47u IPv6 123457 0t0 TCP 192.168.1.10:8080->192.168.1.20:52345 (ESTABLISHED)
这比netstat和ss强的地方在于,lsof -i :端口号不仅能显示监听中的进程,还能显示这个端口上所有已建立的连接,对排查“端口被大量连接占满”的场景特别有用。比如你想知道有没有来自特定IP的连接,直接lsof -i :8080 | grep 192.168.1.20就能过滤。
但如果系统上没装lsof,需要先安装:
bash复制apt install lsof # Debian/Ubuntu
yum install lsof # CentOS/RHEL
值得留意的是,lsof -i :8080和lsof -i TCP:8080效果一样,但如果端口上既有TCP又有UDP服务,lsof -i :8080会同时列出来,不加协议类型时要注意区分。
三条命令里,我日常用得最多的是ss -tlnp,因为快、干净;遇到要看具体连接的IP分布,或者确认端口上活跃连接数,再用lsof -i :端口。习惯之后,其实只用ss -tlnp也能覆盖90%以上的场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从端口到进程:PID、进程名、可执行文件路径一个都不能少
知道端口被哪个进程占了只是第一步,很多时候还得继续深挖:这个进程是谁拉起来的、它的可执行文件在哪、是不是可疑进程、能不能直接动。这一节把反查进程信息的几个方法串起来讲。
2.1 用进程名倒推启动命令和路径
拿到PID之后,先看进程的启动命令行:
bash复制ps -ef | grep 3456
或者用更漂亮的输出:
bash复制ps -fp 3456
输出会给出完整的命令行,内容包括启动参数、配置文件路径、工作目录等。假设输出是:
bash复制UID PID PPID C STIME TTY TIME CMD
root 3456 2234 0 10:22 ? 00:00:12 java -Xms512m -Xmx1024m -jar /opt/app/demo.jar --server.port=8080
这时候基本能判断是哪个应用占用了端口。如果命令行信息不够,比如进程名被故意改成[kworker]或[jbd2],就要看可执行文件路径:
bash复制ls -l /proc/3456/exe
输出会显示该进程实际运行的可执行文件。
/proc/PID/exe是一个软链接,指向进程的主程序文件。对内核线程来说,这个文件通常是[kworker/0:1]这种中括号形式,说明这不是普通用户态进程,属于内核的一部分,遇到这种不要轻易kill。
2.2 查看进程的工作目录和打开的文件
有时候光看命令行还不足以判断这个进程是不是你服务的一部分,比如某些嵌入式的服务会调用/usr/bin/python,但真实脚本路径藏在参数里。这时可以用pwdx看工作目录:
bash复制pwdx 3456
工作目录能帮你找到这个进程从哪个项目里启动。再配合ls -l /proc/3456/fd查看进程打开的文件描述符,比如发现它还打开了一个/etc/nginx/nginx.conf,基本可以确认是nginx在监听。
bash复制ls -l /proc/3456/fd | grep -E "socket|log|conf"
输出里socket:[inode号]就是它占用的socket,如果lsof没装,这个办法依然可以手工核对端口对应的inode。
2.3 用fuser快速定位占用某个端口的进程
fuser是另一个实用的工具,用法非常简洁:
bash复制fuser -v 8080/tcp
输出示例:
bash复制 USER PID ACCESS COMMAND
8080/tcp: root 3456 F.... java
-v是verbose模式,显示进程的用户、PID和访问类型。如果只想静默获取PID,用于脚本判断:
bash复制fuser 8080/tcp
它会直接输出PID,用命令替换就能拿到:
bash复制pid=$(fuser 8080/tcp 2>/dev/null | awk '{print $1}')
fuser还可以直接杀进程:
bash复制fuser -k 8080/tcp
但这里强烈不建议上来就-k,必须确认进程身份后再动手,否则很可能误杀系统服务。实际中不少新手问我“为什么执行了fuser -k就没法SSH了”,多半是把22端口也顺手kill了,连自己都断在外面。
2.4 端口占用与进程的对应关系:通过socket inode核对
如果你面对的是老系统、没有lsof也没有fuser,只有netstat -tlnp却看不到进程PID(比如权限不足),还有最后一招:通过socket的inode号核对。
bash复制netstat -tlnp
输出中如果PID列显示为空或者-,说明当前用户权限不够。可以先拿到监听地址的socket inode:
bash复制ls -l /proc/net/tcp /proc/net/tcp6
/proc/net/tcp里记录了TCP连接哈希表,格式比较复杂。直接讲解解析过程太枯燥,这里给一个实际操作的方案:先通过ss -tlnp看完整输出,如果它也不显示PID,再用root执行ss -tlnp。绝大多数情况下,root权限下能看到所有进程的PID,不需要手工解析inode。
只有当ss和netstat都显示不出来,比如进程属于其他PID命名空间,像是Docker容器内的进程,才会需要进一步从宿主机上匹配inode。这种场景在下一节的踩坑部分细说。
3. 端口占用的处置决策:杀还是不杀,怎么杀才不误伤
端口被占用的最终归宿往往是“把这个进程处理掉”,但处理方式分三六九等,不同情况下“杀”的策略完全不同。这里从确认进程可信度开始,到安全kill、优雅停服、再到处理不了的僵持状态,逐层展开。
3.1 先确认再动手:查看进程的父子关系与启动时间
看到PID之后,先看进程树:
bash复制ps -ef --forest | grep -B5 -A5 3456
--forest会用树状缩进显示父子关系,能直观看到这个进程是由谁拉起来的,父进程是systemd、sshd还是某个shell。假设是systemd拉起来的,那么它可能是某个正在运行的服务,重启或停止应该用systemctl,而不是直接kill。假设是sshd拉起来的,那么它是用户登录后运行的任务,可能是测试时没有退出的java进程,直接kill问题不大。
启动时间也很关键:
bash复制ps -o lstart,cmd -p 3456
输出:
bash复制STARTED CMD
Tue Oct 10 10:22:33 2023 java -Xms512m -Xmx1024m -jar /opt/app/demo.jar --server.port=8080
如果启动时间是几天前,而你现在是要部署新版本,那它很可能是一个遗留的旧进程,没被脚本清理干净。如果启动时间刚刚好是几分钟前,那可能是别人在你并发操作时部署的服务,直接kill反倒破坏了别人的工作。所以在生产环境动进程前,至少确认三件事:进程是谁拉起的、启动时间、启动命令是什么。
3.2 优雅停止优先于强杀,kill -9留给最后
正常推荐顺序:
bash复制kill 3456 # 发送SIGTERM,请求进程自行退出
等待几秒,再确认进程是否退出:
bash复制ss -tlnp | grep :8080
如果进程没有退出,再考虑SIGKILL:
bash复制kill -9 3456
kill -9直接让内核把进程强制终止,不给任何清理资源、保存状态的机会。对于Java、数据库这类程序,强行kill可能导致数据文件损坏或残留临时文件。所以能用SIGTERM解决的问题,不要一开始就上SIGKILL。
有经验的运维还会先试试systemd管理的服务:
bash复制systemctl status 3456
如果进程由systemd管理,用systemctl stop <服务名>会按Unit定义的方式优雅停止,而不是简单地发信号。
3.3 清理残留进程:同端口存在多个进程的情况
一个端口通常只允许一个进程监听,但有一种特殊情况:多个工作进程通过SO_REUSEPORT共享同一个端口,比如nginx的worker_processes、部分高并发代理服务。这种情况下netstat或ss可能显示同一端口被多个PID同时监听。
bash复制ss -tlnp | grep :8080
输出:
bash复制LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=1000,fd=6),("nginx",pid=1001,fd=6),("nginx",pid=1002,fd=6))
这时候如果你只想停掉其中一个,直接用kill某个PID可能并不会释放端口,因为其他worker还拿着同一个监听socket。正确做法是停止整个服务,比如:
bash复制systemctl stop nginx
如果它不是systemd服务,就找主进程(PID最小的通常就是master)发送SIGTERM,让它自己管理worker退出。
3.4 kill后端口仍然被占:TIME_WAIT、孤儿进程与内核中的连接
有一种非常常见的“假占用”:服务进程已经kill了,但ss -tlnp依然能看到端口处于某个状态,或者说连接还在。这里要区分两个概念:监听状态和连接状态。
如果是TIME_WAIT状态:
bash复制ss -tanp | grep :8080
输出:
bash复制TIME-WAIT 0 0 192.168.1.10:8080 192.168.1.20:52345
TIME_WAIT是TCP四次挥手后主动关闭方进入的等待状态,用来确保网络中残留的报文不会影响新连接,默认持续60秒。它不会阻止新服务绑定端口,所以看到TIME_WAIT完全不用紧张,直接启动新服务就行。
如果是孤儿进程,也就是父进程已退出、子进程还在运行,需要用ps -ef找到残留的PID再处理。这类问题多出现在shell脚本里启动后台任务后脚本提前退出,子进程变成孤儿但依然持有端口。
还有一种情况比较隐蔽:进程kill了,端口依然显示LISTEN。通常是因为这个socket被多进程共享,其他进程还持有同一个文件描述符。排查方法:
bash复制ls -l /proc/*/fd 2>/dev/null | grep "socket:\[123456\]"
这里的123456是之前ss输出中Peer Address下面的socket inode号。把每个进程的fd列表过一遍,找到持有这个inode的所有进程,逐个处理,才能彻底释放端口。这比盲目kill一个PID可靠得多。
4. 批量端口、远程端口与脚本化:端口排查的进阶玩法
单端口排查是基本功,但实际工作中往往要处理更复杂的情况:要检查一大批端口是否被占、要探测远程机器上的端口是否可达、还要把排查逻辑写进自动化脚本。这一节从单端口到多端口、再到远程探测和脚本自动化,逐个展开。
4.1 多端口批量检测:用shell循环代替人肉操作
手动执行ss -tlnp | grep :8080,这样一条条查没问题,但当需要检查十几个端口时,效率就有问题了。直接给一个可复用的脚本模板。
bash复制#!/bin/bash
ports=(8080 8081 9090 3306 6379)
for port in "${ports[@]}"; do
result=$(ss -tlnp "sport = :$port" 2>/dev/null)
if [ -n "$result" ]; then
echo "端口 $port 被占用:"
echo "$result"
else
echo "端口 $port 空闲"
fi
done
这里有个细节值得说明:ss "sport = :$port"配合-tln使用,比ss -tlnp | grep :$port更精准,不会把本地地址恰好包含该数字的无关连接匹配出来。单条命令也可以直接写成:
bash复制ss -tlnp 'sport = :8080 or sport = :9090'
ss支持多个条件用or连接,适合临时手查几个端口。
如果希望看到某个端口上有多少条已建立的连接(不是监听),可以这样:
bash复制ss -tanp | grep ":8080" | grep ESTAB | wc -l
这在排查端口被大量连接打满的情况时很有用。
4.2 检测远程端口:nc、nmap与超时控制
有时候端口没在本机占用,但需要确认远程服务器上的某个端口是否开放,比如确认防火墙是否放行了某个服务端口。最朴素的方法是nc:
bash复制nc -zv 192.168.1.20 3306
-z表示扫描模式,不发送数据,只探测端口是否可达;-v输出详细信息。连通时会提示Connection to 192.168.1.20 3306 port [tcp/mysql] succeeded!,不通时返回非零退出码。
但nc在某些轻量系统上不存在,尤其是一些精简Docker镜像里。可以用系统自带的/dev/tcp特性检测:
bash复制timeout 3 bash -c 'echo > /dev/tcp/192.168.1.20/3306' && echo "端口可达" || echo "端口不可达"
这是bash内置的网络重定向功能,不依赖额外工具,但只适合TCP端口。timeout 3是为了防止连接挂起超时。
如果要扫描一段端口,用nmap最方便:
bash复制nmap -p 8000-9000 192.168.1.20
nmap输出更规整,能区分open、closed、filtered三种状态。不过nmap属于重量级工具,生产环境不一定装了,用法也偏渗透测试风格,日常排查我一般先用nc或/dev/tcp。
4.3 免安装脚本:一键统计本机TCP监听端口列表
结合ss的文本输出,可以写一个零依赖的脚本,列出本机所有监听中的TCP端口和对应进程。类似以下:
bash复制ss -tlnp | awk 'NR>1 {
split($4, a, ":");
port = a[length(a)];
match($0, /pid=[0-9]+/);
if (RSTART) pid = substr($0, RSTART+4, RLENGTH-4);
else pid = "unknown";
print port, $6, pid;
}' | sort -n
这个脚本的原理是:ss -tlnp输出的第4列是本地地址IP:端口,用awk切分拿到端口号,再从users:((...))中提取pid=后面的数字。效果就是每行一个端口,方便导入其他监控工具或做巡检。
有了这个基础,把它包装成一个带文件输出和异常告警的巡检脚本也不难,比如检查某几个端口与预期进程是否匹配,不匹配就输出日志。类似这种“预期端口-进程映射”校验,是很多内部运维平台的雏形。
4.4 非root用户看不到PID怎么办
这是个常见尴尬:用普通用户执行ss -tlnp,Process列全是users:(("nginx",pid=...,fd=...))但有时候直接是空。原因是读取其他进程的信息需要权限。这时候有几种选择:
- 切换到root执行(有sudo权的话):
bash复制sudo ss -tlnp
- 查看所有用户的socket关系(不需要PID时):
bash复制ss -tln
- 没有sudo时,只能从
/proc/net/tcp看到端口和inode,再想办法匹配进程。但多数公司运维会给有需要的工程师sudo权限,实在不行就提工单让管理员来看。
权限问题的核心还是遵从一个原则:能用sudo临时提权就提,不要试图绕过权限去读别人的进程信息,一方面技术上不可靠,另一方面也不符合最小权限的安全要求。
4.5 远程端口探测与本地端口占用的区别
很多新手容易混淆“远程端口不可达”和“本地端口被占用”是两回事。本地端口被占用是socket绑定失败,通常报错Address already in use;远程端口不可达则可能是网络不通、防火墙拦截、对端服务没启动等多种原因。
排查远程端口不可达时的正确顺序:
- 先ping目标IP,排除基础网络问题。
- 再
telnet 目标IP 端口或nc -zv 目标IP 端口,确认端口是否开放。 - 然后在目标机器上执行
ss -tlnp,确认服务是否真的在监听。 - 最后检查防火墙和云平台安全组。
如果对端防火墙拦了,nc会显示Connection timed out或No route to host。如果是安全组规则不允许,通常也是超时。碰到这种情况,回到服务端本机先确认监听状态,否则容易在错误的方向上排查很久。
5. 排查端口占用时最容易翻车的几个场景
前面把工具和方法讲完了,这一节挑几个真实遇到的场景细讲。这些案例不是从文档里抄来的,都是实际服务器上出现过的问题,过程还原得比较细,方便对照自己的环境排查。
5.1 场景一:明明是8080被占用,杀掉进程后端口还是被占的假象
某次部署Java应用,日志报8080端口被占用,ss -tlnp显示:
bash复制LISTEN 0 100 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=5012,fd=29))
直接kill 5012,再查ss -tlnp | grep :8080,发现端口确实空了。但几分钟后新进程起来,又报同样的错误。反复几次才发现,原来是启动脚本里有一个旧的java进程作为守护进程,5012只是它的子进程,父进程发现子进程挂了后自动拉起新的。真正的元凶是父进程,不能用kill 5012了事,得按进程树往上找:
bash复制ps -ef --forest | grep java
看到父进程PID后,要么正常停掉服务,要么把守护进程一并处理。
这个案例的教训是:单看占用端口的PID不一定够,必须结合进程树和启动方式判断进程间关系。特别是脚本里用nohup java -jar xxx.jar &启动的服务,如果没有正确管理PID,残留进程很容易让人反复踩坑。
5.2 场景二:Docker容器里的端口占用了宿主机的排查方向
Docker场景下端口占用经常让人困惑。比如宿主机上执行ss -tlnp,看到某个端口被docker-proxy占用:
bash复制LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=7890,fd=4))
这时候端口是被容器映射占用的,真正的服务进程在容器内部。排查逻辑是:
bash复制docker ps | grep 8080
找到容器ID或名称后,进容器看进程:
bash复制docker exec -it <容器ID> ss -tlnp
也可能出现另一种情况:宿主机上的进程想监听8080,但8080已经被docker-proxy占用,看到的就是占用进程是docker-proxy。这时要么改宿主机程序的端口,要么删掉容器的端口映射再重新映射。尤其要注意,如果容器是用-p 8080:80方式映射的,那么宿主机8080上的进程可能是docker-proxy,也可能是跑在host网络模式下的容器进程,后者直接显示容器内进程名。
遇到docker和宿主机端口冲突,建议直接梳理容器端口映射表:
bash复制docker ps --format "table {{.Names}}\t{{.Ports}}"
再结合ss -tlnp判断占用的进程到底属于宿主机还是容器,比瞎猜省时间。
5.3 场景三:ss输出显示UDP端口被占用但找不到进程
TCP端口占用好查,UDP端口被占用时反而容易翻车。比如某次排查一个日志采集服务的问题,ss -ulnp | grep :5140显示:
bash复制UNCONN 0 0 0.0.0.0:5140 0.0.0.0:* users:(("rsyslogd",pid=1234,fd=8))
这是rsyslog占用,但有时候输出是:
bash复制UNCONN 0 0 0.0.0.0:5140 0.0.0.0:*
没有PID,Process列为空,即使root执行也一样。原因很可能是该socket被一个已经消失的进程使用着,或属于某个特殊的网络命名空间。这时候要确认端口是否真的被占用,可以看内核UDP表:
bash复制cat /proc/net/udp | grep -i ":1410"
端口的十六进制表示容易看错,5140的十六进制是1410。再配合socket inode去/proc/*/fd里找持有者,或者直接看ss -ulnp sport = :5140能不能显示更多信息。
UDP无连接的特性也导致netstat和ss在显示上有些不同,实际工作中建议优先用ss -ulnp,然后结合业务日志判断是否是预期的采集器在监听。
5.4 场景四:防火墙导致端口“看起来没占用却连不上”
还有一种情况不是端口被占用,而是防火墙拦截导致新服务无法监听或外部访问不了。比如服务明明已经监听起来了:
bash复制ss -tlnp | grep :9090
LISTEN 0 4096 0.0.0.0:9090 0.0.0.0:* users:(("java",pid=23456,fd=10))
但外部访问不通。排查方法:
- 本机回环测试:
bash复制curl http://127.0.0.1:9090/health
- 外部机器测试:
bash复制nc -zv <服务器IP> 9090
- 检查防火墙规则:
bash复制iptables -L -n | grep 9090
如果回环能通,外部不通,基本是防火墙或云安全组的问题。在CentOS 7以上可能是firewalld:
bash复制firewall-cmd --list-ports
在Debian/Ubuntu上常见的是ufw:
bash复制ufw status
这里提醒一个细节:防火墙拦截和端口占用在日志上表现完全不同。端口占用是服务启动时报Address already in use,防火墙拦截是服务正常启动、但外部访问超时或被拒。两者不要混淆。
5.5 场景五:查看根因时如何从dmesg和系统日志找线索
有些端口占用问题不是服务冲突,而是内核层面的参数限制。比如端口耗尽导致新连接无法建立,虽然表面上也是“端口相关”,但问题不在监听端口,而在连接端口范围。最常见的表现是大量TIME_WAIT累积,或Cannot assign requested address错误。
bash复制dmesg | tail -n 50
查看是否有:
bash复制nf_conntrack: table full, dropping packet
这表示连接跟踪表满了,新连接会被丢弃。这时候调整内核参数可以缓解:
bash复制sysctl net.netfilter.nf_conntrack_max
sysctl net.netfilter.nf_conntrack_buckets
但调整参数只是治标,根因通常是某个服务短连接过多,或者代理层没有复用连接。这类问题排查起来耗时较长,建议先看ss -tanp | grep ESTAB | wc -l评估当前连接总量,再结合应用日志判断是否是瞬时并发过高。
6. 最后分享几个提升效率的小习惯
工具讲完、案例也复盘完了,最后整理几个我实际工作中一直沿用的习惯,算是对上面所有内容的提炼,但每条都来自现场。
第一,查端口时把ss -tlnp记成肌肉记忆,不要每次都在netstat和ss之间犹豫。现代Linux系统普遍自带ss,参数和netstat差不多,输出格式也接近,迁移成本很低。
第二,不要在多个终端窗口里反复手工执行同一组命令,写一个小脚本或alias。我自己的~/.bashrc里就常驻着两个函数:
bash复制port() { ss -tlnp | grep ":$1" || echo "端口 $1 空闲"; }
killport() { pid=$(ss -tlnp "sport = :$1" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | head -1); [ -n "$pid" ] && kill "$pid" && echo "已发送SIGTERM到 $pid"; }
port 8080查占用,killport 8080优雅停掉占用进程。日常排查效率高很多,还不容易误杀。
第三,养成先确认再动手的习惯。看到端口被占用,先看进程用户、启动时间、命令行,再决定是否kill。特别是在多人共用的服务器上,随意kill可能把别人正在调试的服务弄没了。
第四,把端口占用排查当成理解Linux socket机制的入口。查着查着会自然接触/proc/net/tcp、socket inode、网络命名空间、SO_REUSEPORT这些概念,这些都是后续排查JVM、容器网络、服务网格问题时绕不开的基础。
这一套组合拳打下来,端口占用这件小事基本能应付绝大多数生产场景。希望这些内容能帮你少踩几个坑,也欢迎在实际使用中继续补充更多的经验。
