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. 进程是什么

| 概念 | 是什么 | 比喻 |
|---|---|---|
| 程序 | 硬盘上的一个文件,比如 /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. 前台、后台与结束进程

前台和后台
./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 PID | kill -9 PID | |
|---|---|---|
| 进程能收到通知吗 | 能,可以先保存数据、关闭连接再退出 | 不能,直接被系统干掉 |
| 进程能拒绝吗 | 能(程序卡死时可能不响应) | 不能 |
| 什么时候用 | 默认先用这个 | kill 之后等几秒还在,才用 |
后端原则:先
kill,不行再kill -9。直接-9可能导致正在写的数据写一半、数据库连接没关闭。
结束之后要确认它真的没了:
pgrep -af counter.sh # 没有输出 = 已经没有这个进程了
了解即可:
nohup 命令 &让程序在你关掉终端后继续运行。部署单体项目时会细讲,到时候还会学更正规的systemd。
4. 端口是什么

一台服务器上同时跑着很多服务(Web 服务、MySQL、Redis……),它们共用同一个 IP。端口就是用来区分「这个网络请求要交给哪个服务」的编号,范围是 0~65535。
┌─ 端口 80 ─→ Nginx
请求 → 服务器 IP ────────┼─ 端口 3306 ─→ MySQL
├─ 端口 6379 ─→ Redis
└─ 端口 8080 ─→ 你写的后端服务
比喻:IP 是一栋大楼的地址,端口是门牌号。
一个端口同一时间只能被一个进程「监听」(占用)。第二个进程再想用同一个端口,就会报 Address already in use。
后端必须记住的几个默认端口:
| 端口 | 服务 |
|---|---|
| 22 | SSH(远程登录服务器) |
| 80 / 443 | HTTP / HTTPS 网站 |
| 3306 | MySQL |
| 6379 | Redis |
| 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 -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课进程与端口