【Linux从入门到精通】第12篇:进程的前后台切换与信号控制

目录

一、引言:那个被关闭终端杀死的程序

二、前台与后台:终端里的"舞台"与"后台"

[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 完整操作流程演示)

三、kill:不是"杀死",是"发信号"

[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

执行后,你的终端被这个命令"占据"了------光标闪烁,但你输入任何东西都没反应。此时你有三个选择:

  1. 耐心等它执行完

  2. Ctrl+C强制终止它

  3. 把它"甩"到后台去

前台进程后台进程的核心区别:

对比维度 前台进程 后台进程
占用终端 是,执行期间无法输入其他命令 否,终端可以继续使用
接收键盘输入 可以 不能(尝试读取会暂停)
被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 继续运行 bgfg 唤醒暂停进程

3.4 killall和pkill:按名称发信号

如果每次都先ps找PID再kill,效率太低。killallpkill允许你直接用进程名发信号:

bash

复制代码
# 优雅终止所有nginx进程
killall nginx

# 强制杀死所有python进程
killall -9 python

# pkill支持模糊匹配
pkill -f "python train"   # 匹配包含"python train"的完整命令行

警告killall会向所有匹配名称的进程发送信号,务必确认没有误伤重要进程。


四、终端关闭后如何保持程序运行

回到开篇的问题:SSH断开后,怎么让程序继续跑?

解决方案有两种思路:

  • 轻量级nohup,适合临时跑一个长任务

  • 重量级screentmux,适合需要持续交互的复杂场景

4.1 nohup:最简单的保活方案

nohup(no hang up)的作用是:让进程忽略SIGHUP信号。终端关闭时系统发送的正是这个信号。

bash

复制代码
nohup 命令 &

标准用法

bash

复制代码
nohup python train_model.py > output.log 2>&1 &

这条命令做了三件事:

  1. nohup:忽略SIGHUP,终端关了也不影响

  2. > output.log 2>&1:把标准输出和错误输出都重定向到文件(nohup默认会生成nohup.out,这里指定自己的日志文件)

  3. &:放到后台运行

执行后会显示:

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 -hdu -sh 如何排查磁盘空间问题

  • lsblk 查看磁盘和分区信息

  • mount / umount 挂载U盘和ISO镜像

  • /etc/fstab 实现开机自动挂载

你会学会如何给Linux"加硬盘",以及当磁盘空间告急时如何快速定位是哪些文件在占地方。


延伸思考 :试试执行kill -l,数一数有多少个信号。其中有没有你完全看不懂名字的?别担心,即使是资深运维,日常用到的也就五六个。关键是理解"信号是一种进程间通信方式"这个核心概念。

相关推荐
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 84. 柱状图中最大的矩形 | C++ 两次单调栈基础扫法
c++·算法·leetcode
Cyan_RA92 小时前
如何利用 Paddle-OCR 丝滑进行复杂版面 PDF 的批量化OCR处理?
java·linux·python·ocr·conda·paddle·surya
AC赳赳老秦2 小时前
OpenClaw与Notion联动:自动同步工作任务、整理笔记,实现高效管理
运维·人工智能·python·数学建模·自动化·deepseek·openclaw
上海云盾安全满满2 小时前
服务器带宽一直跑高,是什么情况
运维·服务器
.柒宇.2 小时前
信创实战:银河麒麟 V10 服务器安装、网络配置与 Docker 环境搭建
linux·运维·docker·国产信创·麒麟操作系统
C雨后彩虹2 小时前
文件目录大小
java·数据结构·算法·华为·面试
云动课堂2 小时前
【运维实战】企业级SFTP 文件服务 · 一键自动化部署方案 (适配AnolisOS /openEuler /CentOS)
运维·centos·自动化
杨浦老苏2 小时前
Docker容器管理面板Dockhand
运维·docker·群晖
0南城逆流02 小时前
【技术点】嵌入式技术考点三:数据结构
java·数据结构·算法