目录
[2.1 基本概念](#2.1 基本概念)
[2.2 Ctrl+Z:暂停并放入后台](#2.2 Ctrl+Z:暂停并放入后台)
[2.3 jobs:查看后台作业](#2.3 jobs:查看后台作业)
[2.4 bg:让暂停的后台作业继续运行](#2.4 bg:让暂停的后台作业继续运行)
[2.5 fg:把后台作业调回前台](#2.5 fg:把后台作业调回前台)
[2.6 &:直接后台启动](#2.6 &:直接后台启动)
[2.7 完整操作流程演示](#2.7 完整操作流程演示)
[3.1 一个常见的误解](#3.1 一个常见的误解)
[3.2 最常用的两个信号:15和9](#3.2 最常用的两个信号:15和9)
[3.3 其他常用信号](#3.3 其他常用信号)
[3.4 killall和pkill:按名称发信号](#3.4 killall和pkill:按名称发信号)
[4.1 nohup:最简单的保活方案](#4.1 nohup:最简单的保活方案)
[4.2 screen:终端会话的"保存与恢复"](#4.2 screen:终端会话的“保存与恢复”)
[4.3 tmux:screen的现代化替代品](#4.3 tmux:screen的现代化替代品)
一、引言:那个被关闭终端杀死的程序
有没有遇到过这种场景:你用SSH连上服务器,启动了一个需要跑很久的脚本,然后你关闭了笔记本盖子去吃饭。回来后重新连上服务器,发现脚本没了------它在你断开SSH的那一刻就被系统杀掉了。
这不是bug,而是Linux的设计机制:当终端关闭时,系统会向该终端启动的所有进程发送SIGHUP信号,默认行为是终止进程。
理解这个机制,并学会让程序在"后台"独立运行,是运维和开发中的必修课。今天我们就来解决这个问题。
二、前台与后台:终端里的"舞台"与"后台"
2.1 基本概念
当你打开终端执行一条命令时,默认是"前台运行":
bash
sleep 100
执行后,你的终端被这个命令"占据"了------光标闪烁,但你输入任何东西都没反应。此时你有三个选择:
-
耐心等它执行完
-
按
Ctrl+C强制终止它 -
把它"甩"到后台去
前台进程 和后台进程的核心区别:
| 对比维度 | 前台进程 | 后台进程 |
|---|---|---|
| 占用终端 | 是,执行期间无法输入其他命令 | 否,终端可以继续使用 |
| 接收键盘输入 | 可以 | 不能(尝试读取会暂停) |
| 被Ctrl+C终止 | 会 | 不会 |
| 终端关闭时 | 会被杀死 | 也会被杀死(除非用特殊手段) |
2.2 Ctrl+Z:暂停并放入后台
Ctrl+Z的作用是:暂停当前前台进程,并将其放入后台作业队列。
bash
sleep 100
# 按 Ctrl+Z
你会看到类似输出:
text
[1]+ Stopped sleep 100
此时sleep进程并没有死,而是处于暂停状态,等待你的下一步指令。
2.3 jobs:查看后台作业
jobs命令列出当前终端会话中的所有后台作业:
bash
jobs
# 输出示例:
# [1]- Stopped sleep 100
# [2]+ Running python train.py &
每行开头的[1]是作业号,这是你后续操作该作业的"句柄"。
2.4 bg:让暂停的后台作业继续运行
bg(background)让一个已暂停的后台作业在后台继续运行:
bash
bg %1 # 让作业号为1的作业在后台继续运行
bg # 不带参数,默认操作最近一个作业(带+号的)
注意%1这个写法------在操作后台作业时,需要用%加作业号来指定目标,以区别于普通命令的参数。
2.5 fg:把后台作业调回前台
fg(foreground)把后台作业拉回前台运行:
bash
fg %1 # 把作业1调回前台
fg # 默认操作最近一个作业
调回前台后,这个作业会重新占据你的终端,你可以与它正常交互,也能用Ctrl+C终止它。
2.6 &:直接后台启动
如果你从一开始就知道某个命令要跑很久,可以在命令末尾加&,让它直接后台启动:
bash
sleep 100 &
# 输出:[1] 12345 (方括号内是作业号,后面是PID)
这样启动的进程直接处于运行状态 ,不需要再按Ctrl+Z然后bg。
2.7 完整操作流程演示
bash
# 场景:启动一个长时间任务,中途想临时做点别的事
# 1. 前台启动任务
python train_model.py
# 2. 按 Ctrl+Z 暂停并放入后台
# 看到 [1]+ Stopped
# 3. 让它在后台继续跑
bg %1
# 4. 干点别的事(比如查看日志)
tail -f training.log
# 5. 想看训练进度了,把任务拉回前台
fg %1
# 6. 不需要了,Ctrl+C 终止
三、kill:不是"杀死",是"发信号"
3.1 一个常见的误解
很多初学者以为kill就是"杀死进程"的命令。但实际上,kill的真实作用是:向进程发送信号。至于收到信号后进程做什么,取决于信号的类型和进程自身的处理逻辑。
bash
kill -l # 列出所有支持的信号
你会看到64个不同的信号,从1到64各有不同的含义。"终止进程"只是其中一部分信号的功能。
3.2 最常用的两个信号:15和9
| 信号编号 | 信号名 | 含义 | 进程能否捕获/忽略 |
|---|---|---|---|
| 15 | SIGTERM | 终止信号(Terminate) | 能 |
| 9 | SIGKILL | 强制杀死(Kill) | 不能 |
SIGTERM(15)------优雅退出
这是kill命令的默认信号。当你执行kill PID时,实际上发送的是SIGTERM。
收到SIGTERM的进程可以:
-
执行清理工作(保存数据、关闭文件、释放资源)
-
决定要不要退出
-
甚至完全忽略这个信号
大多数编写规范的程序在收到SIGTERM后会优雅退出------先完成手头的工作,再自行结束。这就是为什么推荐先用kill PID试试,不行再用kill -9。
SIGKILL(9)------强制终止
kill -9 PID发送的是SIGKILL信号。这个信号进程无法捕获、无法忽略、无法执行任何清理操作------内核直接终止进程。
它是最"暴力"的手段,应该作为最后的选择。因为:
-
进程没机会保存数据,可能导致文件损坏
-
进程持有的锁不会被释放,可能导致其他进程卡住
-
共享内存可能残留,需要手动清理
3.3 其他常用信号
| 信号 | 编号 | 作用 | 典型场景 |
|---|---|---|---|
| SIGHUP | 1 | 挂断信号 | 重新加载配置文件(nginx -s reload) |
| SIGINT | 2 | 中断信号 | Ctrl+C 就是发送这个信号 |
| SIGKILL | 9 | 强制杀死 | 进程卡死,其他方式无效时的最后手段 |
| SIGTERM | 15 | 终止信号 | 正常的"请退出"请求 |
| SIGSTOP | 19 | 暂停进程 | Ctrl+Z 的底层实现 |
| SIGCONT | 18 | 继续运行 | bg 或 fg 唤醒暂停进程 |
3.4 killall和pkill:按名称发信号
如果每次都先ps找PID再kill,效率太低。killall和pkill允许你直接用进程名发信号:
bash
# 优雅终止所有nginx进程
killall nginx
# 强制杀死所有python进程
killall -9 python
# pkill支持模糊匹配
pkill -f "python train" # 匹配包含"python train"的完整命令行
警告 :
killall会向所有匹配名称的进程发送信号,务必确认没有误伤重要进程。
四、终端关闭后如何保持程序运行
回到开篇的问题:SSH断开后,怎么让程序继续跑?
解决方案有两种思路:
-
轻量级 :
nohup,适合临时跑一个长任务 -
重量级 :
screen或tmux,适合需要持续交互的复杂场景
4.1 nohup:最简单的保活方案
nohup(no hang up)的作用是:让进程忽略SIGHUP信号。终端关闭时系统发送的正是这个信号。
bash
nohup 命令 &
标准用法:
bash
nohup python train_model.py > output.log 2>&1 &
这条命令做了三件事:
-
nohup:忽略SIGHUP,终端关了也不影响 -
> output.log 2>&1:把标准输出和错误输出都重定向到文件(nohup默认会生成nohup.out,这里指定自己的日志文件) -
&:放到后台运行
执行后会显示:
text
[1] 12345
现在你可以放心关闭终端或断开SSH,进程会继续运行。回来后想看进度:
bash
tail -f output.log
想终止它:
bash
kill 12345 # 用之前显示的PID
4.2 screen:终端会话的"保存与恢复"
nohup的局限在于:一旦放到后台,你就无法再和程序交互了 。如果训练脚本需要中途输入参数,nohup无能为力。
screen解决了这个问题------它创建一个独立的终端会话,你可以随时"分离"(detach)和"重新接入"(attach),就像保存游戏进度一样。
安装:
bash
sudo apt install screen -y # Ubuntu/Debian
sudo dnf install screen -y # CentOS/RHEL
核心操作流程:
bash
# 1. 创建一个命名会话
screen -S train
# 2. 在新窗口中执行你的命令
python train_model.py
# 3. 按 Ctrl+A 然后按 D —— 分离会话(Detach)
# 你会回到原来的终端,train会话在后台继续运行
# 4. 断开SSH,吃饭,睡觉,回来重新连接
# 5. 重新接入之前的会话
screen -r train
# 6. 你又看到了正在运行的训练脚本!就像从未离开过
常用命令速查:
| 命令 | 作用 |
|---|---|
screen -S 名称 |
创建命名会话 |
screen -ls |
查看所有会话 |
screen -r 名称 |
重新接入会话 |
screen -d 名称 |
强制分离会话(如果卡住了) |
screen -X -S 名称 quit |
彻底结束会话 |
screen内部快捷键 (先按Ctrl+A,再按下一个键):
| 快捷键 | 作用 |
|---|---|
Ctrl+A D |
分离会话(最常用) |
Ctrl+A K |
杀死当前窗口 |
Ctrl+A C |
创建新窗口 |
Ctrl+A N |
切换到下一个窗口 |
Ctrl+A " |
列出所有窗口 |
4.3 tmux:screen的现代化替代品
tmux功能比screen更强大,支持窗口分割、更灵活的配置。但对于"保活长任务"这个需求,screen足够用。如果你以后需要更复杂的终端复用功能,可以去了解tmux。
五、综合实战:一个完整的远程训练任务
假设你要在服务器上训练一个深度学习模型,预计要跑24小时。完整操作流程如下:
第一步:创建screen会话
bash
ssh user@server
screen -S model_training
第二步:启动训练
bash
cd ~/project
python train.py --epochs 100 2>&1 | tee training.log
(tee让我们既能实时看输出,又能保存到文件)
第三步:分离会话
text
按 Ctrl+A 然后按 D
看到[detached]提示,回到普通终端。
第四步:断开SSH,回家睡觉
第五步:第二天重新连接查看
bash
ssh user@server
screen -r model_training
你又看到了训练进度!就像从未离开过。
第六步:如果训练已完成,结束会话
在screen内输入exit或按Ctrl+A K。
六、本篇小结
今天掌握了进程控制的三个核心技能:
前后台切换:
-
Ctrl+Z:暂停前台任务并放入后台 -
bg:让暂停的任务在后台继续运行 -
fg:把后台任务拉回前台 -
jobs:查看当前会话的后台作业
信号控制:
-
kill的本质是"发送信号",不是"杀死" -
先用
kill PID(SIGTERM),程序可优雅退出 -
实在不行再用
kill -9 PID(SIGKILL)
进程保活:
-
nohup 命令 &:临时防断线,不需要交互 -
screen -S 会话名:需要随时接入交互的复杂任务
选择建议:
| 场景 | 推荐方案 |
|---|---|
| 跑一个脚本,跑完就完事,不需要看进度 | nohup ... & |
| 跑一个长任务,想随时看进度/暂停/改参数 | screen |
| 一次性命令,但想让它在后台跑,不影响继续工作 | command & |
动手练习
bash
# 1. 练习前后台切换
sleep 200 &
jobs
fg %1
# 按 Ctrl+Z 暂停
bg %1
kill %1 # 用作业号也能kill
# 2. 体验信号区别
# 启动一个能捕获信号的进程(如top)
top
# 另一个终端执行
kill PID # top正常退出
kill -9 PID # 再次启动top,这次用-9,观察差异
# 3. 练习screen
screen -S test
# 在screen内执行:top
# 按 Ctrl+A D 分离
screen -ls # 查看会话列表
screen -r test # 重新接入
# 在screen内按 q 退出top,然后 exit 退出screen
# 4. 练习nohup
nohup ping localhost > ping.log 2>&1 &
# 关闭终端,重新打开
tail -f ping.log # 确认ping仍在运行
killall ping # 清理
七、下篇预告
掌握了进程的查看与控制,下一篇我们将关注另一个系统核心资源------磁盘。
《磁盘管理与文件系统》将带你了解:
-
df -h和du -sh如何排查磁盘空间问题 -
lsblk查看磁盘和分区信息 -
mount/umount挂载U盘和ISO镜像 -
/etc/fstab实现开机自动挂载
你会学会如何给Linux"加硬盘",以及当磁盘空间告急时如何快速定位是哪些文件在占地方。
延伸思考 :试试执行kill -l,数一数有多少个信号。其中有没有你完全看不懂名字的?别担心,即使是资深运维,日常用到的也就五六个。关键是理解"信号是一种进程间通信方式"这个核心概念。