跳到主要内容

Linux 命令初识第 6 课:进程与端口

目标:会用 ps 查看进程、用 kill 结束进程,会把程序放到后台运行;理解「端口」是什么,遇到 Address already in use 能用 ss 找出是哪个进程占用了端口并处理掉。 用时:约 40 分钟。全部在 Ubuntu 里敲。

这一课解决后端开发中最高频的一个报错:

OSError: [Errno 98] Address already in use ← Python
bind: address already in use ← Go
Web server failed to start. Port 8080 was already in use. ← Spring Boot

意思都一样:你要用的端口被别的进程占着。排查思路固定三步:谁占了端口 → 它是什么进程 → 要不要结束它。


0. 准备练习数据​

mkdir -p ~/practice/lesson6/www && cd ~/practice/lesson6
echo '<h1>hello from lesson6</h1>' > www/index.html
printf '#!/bin/bash\nwhile true; do echo "$(date +%%T) count" >> ~/practice/lesson6/counter.log; sleep 1; done\n' > counter.sh
chmod 755 counter.sh
cat counter.sh

counter.sh 是一个永远不会自己结束的脚本:每秒往 counter.log 里写一行当前时间。用它来模拟「一直在跑的后端服务」。

while true; do ...; done 是死循环,sleep 1 是等 1 秒,现在照抄即可。


1. 进程是什么​

PID 与父子进程充实版:概念、进程列含义、ps 与 pgrep 命令

概念是什么比喻
程序硬盘上的一个文件,比如 /usr/bin/python3菜谱
进程程序跑起来之后的实例,占内存、占 CPU照着菜谱正在做的一道菜
PID进程编号,系统里唯一订单号
PPID父进程的 PID,谁启动了它下单人的订单号

同一个程序可以同时跑好几个进程,比如开 3 个终端就有 3 个 bash 进程,PID 各不相同。

第 5 课的「子进程」在这里就看得见了:你在终端里运行的每个命令,都是当前 bash 的子进程,它的 PPID 就是 bash 的 PID。

echo $$ # $$ 是一个特殊变量:当前 bash 自己的 PID

2. ps —— 查看进程​

两种常用写法​

ps aux # 列出系统里所有进程(BSD 风格,最常用)
ps -ef # 同上(System V 风格,能看到 PPID)
ps -f -p 1234 # 只看 PID 为 1234 的那一个进程

ps aux 的输出:

USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
zzc 1234 0.0 0.1 4948 3752 pts/5 S 18:15 0:00 /bin/bash ./counter.sh
↑ ↑ ↑ ↑ ↑ ↑
谁的 编号 CPU占用 内存占用 状态 启动它的完整命令

需要关注的就这几列:USER、PID、%CPU、%MEM、COMMAND。线上排查「服务器为什么这么卡」,第一步就是看谁的 %CPU、%MEM 最高。

配合管道找某个进程​

ps aux | grep counter.sh
zzc 127721 ... /bin/bash ./counter.sh
zzc 127740 ... grep counter.sh ← grep 自己也被列出来了!

⚠️ 这是个经典的坑:grep counter.sh 这个命令的进程里本身就含有 counter.sh 这几个字,所以它会把自己也找出来。两种解决办法:

ps aux | grep counter.sh | grep -v grep # 再用 -v 去掉含 grep 的行(第 3 课学的)
pgrep -af counter.sh # 专门按名字找进程的命令,不会找到自己

pgrep -af 输出 PID 完整命令,比 ps aux | grep 干净。-f 表示按完整命令行匹配,-a 表示把命令行也显示出来。

top —— 实时查看(了解即可)​

top # 像任务管理器,每 3 秒刷新,默认按 CPU 占用排序;按 q 退出

3. 前台、后台与结束进程​

进程管理充实版:前后台、日志重定向、jobs、fg、TERM 和 KILL

前台和后台​

./counter.sh # 前台运行:终端被它占住,不能再输入命令
# 按 Ctrl+C 结束它

./counter.sh & # 后台运行:终端立刻还给你
[1] 127721 # [1] 是任务编号,127721 是 PID

第 4 课说过 & 不是「并且」,而是「放到后台」,这里就是它的正经用法。

操作作用
Ctrl+C结束前台正在运行的程序
命令 &直接在后台启动
jobs列出当前终端里的后台任务
fg把后台任务拉回前台(然后可以 Ctrl+C 结束它)

⚠️ 后台进程的输出还是会打到终端上,把屏幕弄乱。所以后台运行一般配合重定向(第 4 课):

./server > server.log 2>&1 & # 正常输出和报错都进日志文件

kill —— 结束进程​

kill 127721 # 礼貌地请它结束(发送 TERM 信号,编号 15)
kill -9 127721 # 强制杀死(发送 KILL 信号,编号 9)
pkill -f counter.sh # 按名字结束,省得先查 PID(了解即可,小心别误杀)
kill PIDkill -9 PID
进程能收到通知吗能,可以先保存数据、关闭连接再退出不能,直接被系统干掉
进程能拒绝吗能(程序卡死时可能不响应)不能
什么时候用默认先用这个kill 之后等几秒还在,才用

后端原则:先 kill,不行再 kill -9。直接 -9 可能导致正在写的数据写一半、数据库连接没关闭。

结束之后要确认它真的没了:

pgrep -af counter.sh # 没有输出 = 已经没有这个进程了

了解即可:nohup 命令 & 让程序在你关掉终端后继续运行。部署单体项目时会细讲,到时候还会学更正规的 systemd。


4. 端口是什么​

IP 端口与 PID 充实版:监听、地址范围、常用端口和排查命令

一台服务器上同时跑着很多服务(Web 服务、MySQL、Redis……),它们共用同一个 IP。端口就是用来区分「这个网络请求要交给哪个服务」的编号,范围是 0~65535。

┌─ 端口 80 ─→ Nginx
请求 → 服务器 IP ────────┼─ 端口 3306 ─→ MySQL
├─ 端口 6379 ─→ Redis
└─ 端口 8080 ─→ 你写的后端服务

比喻:IP 是一栋大楼的地址,端口是门牌号。

一个端口同一时间只能被一个进程「监听」(占用)。第二个进程再想用同一个端口,就会报 Address already in use。

后端必须记住的几个默认端口:

端口服务
22SSH(远程登录服务器)
80 / 443HTTP / HTTPS 网站
3306MySQL
6379Redis
8080后端服务开发时最常用的端口(Spring Boot 默认)

第 2 课的 /etc/services 里记录的就是这些默认端口。


5. 起一个服务,再找到它​

用 Python 起一个最简单的 Web 服务​

Ubuntu 自带 python3,一条命令就能起一个 Web 服务,把一个目录里的文件通过 HTTP 提供出去:

python3 -m http.server 8080 --directory ~/practice/lesson6/www > /dev/null 2>&1 &
  • 8080:监听的端口
  • --directory:把哪个目录当网站根目录
  • > /dev/null 2>&1:它每收到一个请求就打印一行日志,丢掉,免得刷屏
  • &:放到后台

验证它在工作:

curl http://localhost:8080 # <h1>hello from lesson6</h1>

curl 是命令行里的「浏览器」,发一个 HTTP 请求并打印响应。localhost 指本机。也可以在 Windows 的浏览器里打开 http://localhost:8080,WSL 会自动把端口转发给 Windows。

ss —— 看谁在监听哪个端口​

端口排查充实版:启动验证、ss 参数、输出解释和完整处置流程

ss -lntp
选项含义
-l只看正在监听(listen)的
-n端口显示成数字(不加会显示成 http-alt 这种名字)
-t只看 TCP
-p显示是哪个进程(process)
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 5 0.0.0.0:8080 0.0.0.0:* users:(("python3",pid=127699,fd=3))
↑ ↑ ↑
端口 进程名 PID
  • 0.0.0.0:8080 表示在本机所有网卡上监听 8080。
  • -p 只能看到自己的进程是谁。别的用户(比如 root)的进程,要 sudo ss -lntp 才显示。

查某一个端口,配合 grep:

ss -lntp | grep :8080

lsof —— 另一种查法​

lsof -i :8080
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 127699 zzc 3u IPv4 768881 0t0 TCP *:8080 (LISTEN)

输出更直观,直接有 PID 列。两个都行,ss 几乎每台 Linux 都有,lsof 有时要另装。

了解即可:老教程里常见 netstat -lntp,作用和 ss -lntp 一样,新系统默认不装了。

完整排查流程:端口被占用​

# 1. 启动报错:Address already in use
python3 -m http.server 8080
# OSError: [Errno 98] Address already in use

# 2. 谁占了 8080?
ss -lntp | grep :8080 # 找到 pid=127699

# 3. 它是什么进程?是不是我能结束的?
ps -f -p 127699

# 4. 确认可以结束,再结束它
kill 127699

# 5. 确认端口已经释放(没有输出)
ss -lntp | grep :8080

# 6. 重新启动

⚠️ 第 3 步不能省:占用端口的可能是别人的服务、或者是你另一个终端里还在跑的旧版本。看清楚再杀。


6. 跟着敲一遍​

cd ~/practice/lesson6
echo $$
ps -f -p $$ # 猜:PPID 是谁?
bash # 开一个子 bash
echo $$ # 猜:和上面一样吗?
ps -f -p $$ # 猜:这次的 PPID 是谁?
exit # 退出子 bash
./counter.sh &
jobs
tail -n 3 counter.log
ps aux | grep counter.sh # 猜:几行?
pgrep -af counter.sh
kill %1 # %1 表示 jobs 里的 1 号任务,也可以写 PID
pgrep -af counter.sh # 猜:还有输出吗?
python3 -m http.server 8080 --directory ~/practice/lesson6/www > /dev/null 2>&1 &
curl http://localhost:8080
ss -lntp | grep :8080
lsof -i :8080
python3 -m http.server 8080 # 猜:会怎样?
kill 刚才查到的PID
ss -lntp | grep :8080 # 猜:还有输出吗?

每敲一条,先猜结果,再看输出对不对。


7. 练习题​

题目在本文件夹的 题目解答.md 里。先执行文件开头「练习准备」里的命令,再做题,把每题的命令、输出和回答写在 解答: 下面,做完告诉我,我来批改。


8. 课后​

  • 提交:01-环境准备-01-linux命令初识-第6课进程与端口