Chapter 06

进程管理

掌握 Linux 进程的查看、控制与调度:从 ps/top 到 systemctl 服务管理

进程基础概念

进程(Process)
程序运行时的实例。每个进程有唯一的 PID(Process ID),拥有独立的内存空间、打开的文件、信号处理器等。Linux 用树状结构组织进程,所有进程都是 PID=1 的 init/systemd 的后代。
父进程与子进程
Shell 执行命令时,会 fork() 出子进程来运行该命令。子进程的 PPID(Parent PID)指向父进程。父进程等待子进程退出并回收其退出状态。若父进程先退出,子进程变为"孤儿进程",被 systemd(PID=1) 收养。
进程状态
R(Running/Runnable):运行或等待运行;S(Sleeping):可中断睡眠,等待事件;D(Disk Sleep):不可中断睡眠,等待 I/O;Z(Zombie):已退出但父进程未回收;T(Stopped):被信号暂停;I(Idle):空闲内核线程。
前台与后台进程
前台进程占用终端,用户等待其完成;后台进程在后台运行,终端立即返回提示符。使用 & 符号将命令放入后台,使用 jobs/fg/bg 管理。

查看进程:ps 命令

# BSD 风格(最常用)
ps aux              # 显示所有用户的所有进程(详细信息)
ps aux | grep nginx # 查找 nginx 相关进程
ps aux --sort=-%cpu # 按 CPU 使用率排序(降序)
ps aux --sort=-%mem # 按内存使用率排序

# UNIX 风格
ps -ef              # 显示所有进程(包含 PPID)
ps -ef | grep java
ps -u alice         # 只显示 alice 的进程

# 进程树
ps auxf             # ASCII 树状图显示进程关系
pstree              # 更直观的树状图
pstree -p           # 显示 PID
pstree -u           # 显示用户名

# 查看特定进程
ps -p 1234          # 查看 PID=1234 的进程
ps -p 1234 -o pid,ppid,cmd,rss,vsz  # 自定义输出列
ps aux 列含义: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND alice 1234 2.5 1.2 55420 9820 pts/0 Sl 10:00 0:05 python app.py │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─ 累计 CPU 时间 │ │ │ │ │ │ │ └─ 启动时间 │ │ │ │ │ │ └─ 进程状态 │ │ │ │ │ │─ 终端 │ │ │ │ └─ 实际物理内存 (KB) │ │ │ └─ 内存使用 % │ │ └─ 虚拟内存大小 (KB) │ └─ CPU 使用 % └─ 进程 ID

实时监控:top 与 htop

# top:系统自带的实时监控工具
top                 # 默认按 CPU 使用率排序
top -u alice        # 只显示 alice 的进程
top -p 1234,5678    # 监控特定 PID
top -b -n 1         # 批量模式,输出一次后退出(用于脚本)
top -b -n 1 | head -20  # 取前20行

# top 快捷键(运行中按键)
# k    按 PID 终止进程
# r    修改进程优先级(renice)
# M    按内存排序
# P    按 CPU 排序(默认)
# T    按运行时间排序
# 1    展开/折叠 CPU 核心
# q    退出

# htop:功能更强大的交互式监控(需要安装)
apt install htop
htop
htop -u alice       # 只显示 alice 的进程
htop -p 1234        # 监控特定 PID
top 头部信息解读

信号与 kill 命令

常用信号

SIGTERM(15,默认)
请求进程正常退出。进程可以捕获此信号,做清理工作后再退出(关闭连接、保存状态等)。这是最礼貌的终止方式,应该首先尝试。
SIGKILL(9)
强制立即终止进程。内核直接杀死进程,进程无法捕获或忽略此信号,无法做清理工作。用于进程无响应或拒绝退出时。可能导致数据损坏,谨慎使用。
SIGHUP(1)
挂起信号。历史上用于通知进程终端断开。现代用途:通知进程重新加载配置文件,无需重启(nginx、sshd 等都支持 SIGHUP 热重载)。
SIGSTOP(19)/ SIGCONT(18)
暂停/继续进程执行。Ctrl+Z 发送 SIGTSTP(可被捕获的软暂停),SIGSTOP 不可被捕获。SIGCONT 恢复被暂停的进程。
# kill:按 PID 发送信号
kill 1234           # 默认发送 SIGTERM(15)
kill -15 1234       # 显式 SIGTERM
kill -9 1234        # SIGKILL(强制)
kill -1 1234        # SIGHUP(重载配置)
kill -STOP 1234     # 暂停进程
kill -CONT 1234     # 恢复进程

# killall:按进程名发送信号
killall nginx               # 终止所有名为 nginx 的进程
killall -9 python3          # 强制终止所有 python3 进程
killall -u alice python3    # 只终止 alice 的 python3 进程

# pkill:按名称模式匹配
pkill -f "python app.py"    # 匹配完整命令行
pkill -u alice              # 终止 alice 的所有进程
pkill -9 java               # 强制终止所有 java 进程

# 查看所有信号
kill -l                     # 列出所有信号名

# 正确的终止进程流程
# 1. 先尝试 SIGTERM(给进程清理机会)
kill 1234
# 2. 等待几秒
sleep 3
# 3. 检查是否已退出
ps -p 1234 || echo "进程已退出"
# 4. 仍存在则强制终止
kill -9 1234

后台作业管理

# 在后台启动命令(用 & 符号)
./long-running-task.sh &    # 返回: [1] 12345(作业号和PID)
python server.py &

# 将前台进程发送到后台
# 1. 按 Ctrl+Z 暂停当前前台进程
# 输出: [1]+  Stopped  ./task.sh
bg %1           # 将作业 1 在后台继续运行
bg              # 将最近暂停的作业放入后台

# jobs:查看当前 Shell 的后台作业
jobs            # 列出所有作业
jobs -l         # 显示 PID

# 输出示例:
# [1]-  Running    python server.py &
# [2]+  Stopped    vim notes.txt

# fg:将后台作业调回前台
fg %1           # 将作业 1 调回前台
fg              # 调回最近的作业(+ 标记的)
fg %python      # 按名称匹配

# 等待后台作业完成
wait %1         # 等待作业 1 完成
wait            # 等待所有后台作业

nohup:终端关闭后继续运行

# 问题:SSH 断开后,后台进程会收到 SIGHUP 而终止
# 解决:nohup 让进程忽略 SIGHUP 信号

nohup ./task.sh &                    # 后台运行,输出到 nohup.out
nohup python server.py > app.log 2>&1 &   # 重定向输出
nohup ./deploy.sh > /dev/null 2>&1 &      # 丢弃所有输出

# 查看 nohup 进程(新 SSH 会话后)
ps aux | grep task.sh

# 更好的替代方案:screen 或 tmux
screen -S mysession         # 创建命名会话
# (在 screen 中运行命令)
# 按 Ctrl+A, D 分离会话
screen -ls                  # 列出所有会话
screen -r mysession         # 重新连接会话

# tmux(更现代的方案)
tmux new -s deploy          # 创建名为 deploy 的会话
# 按 Ctrl+B, D 分离
tmux ls                     # 列出会话
tmux attach -t deploy       # 重新连接

进程优先级:nice 与 renice

# Nice 值范围:-20(最高优先级)到 +19(最低优先级)
# 普通用户只能设置 0~19(降低优先级)
# root 可以设置 -20~19

# nice:启动时设置优先级
nice -n 10 ./cpu-intensive-task.sh   # 低优先级运行(让出 CPU)
nice -n -5 ./critical-task.sh        # 高优先级(需要 root)

# renice:修改运行中进程的优先级
renice -n 15 -p 1234        # 降低 PID=1234 的优先级
renice -n -10 -p 1234       # 提高优先级(需要 root)
renice -n 5 -u alice        # 修改 alice 所有进程的优先级

# 查看进程优先级(NI 列)
ps -lo pid,ni,cmd           # 显示 nice 值
top                         # NI 列显示 nice 值,PR 列显示调度优先级

systemctl:系统服务管理

服务的生命周期

# 启动/停止/重启服务
sudo systemctl start nginx      # 启动
sudo systemctl stop nginx       # 停止
sudo systemctl restart nginx    # 重启(先停后启)
sudo systemctl reload nginx     # 热重载配置(不中断连接)

# 开机自启动
sudo systemctl enable nginx     # 开机自启
sudo systemctl disable nginx    # 取消开机自启
sudo systemctl enable --now nginx    # 启动并设置开机自启
sudo systemctl disable --now nginx   # 停止并取消自启

# 查看服务状态
systemctl status nginx          # 详细状态(最近日志、PID、内存等)
systemctl is-active nginx       # 输出 active/inactive/failed
systemctl is-enabled nginx      # 输出 enabled/disabled
systemctl is-failed nginx       # 输出 failed/active

# 列出服务
systemctl list-units --type=service             # 所有活跃服务
systemctl list-units --type=service --all       # 包含未启动的
systemctl list-units --type=service --state=failed  # 失败的服务

# 系统级操作
sudo systemctl reboot           # 重启系统
sudo systemctl poweroff         # 关机
sudo systemctl suspend          # 挂起
sudo systemctl daemon-reload    # 重新加载 systemd 配置(修改 Unit 文件后)

创建自定义服务

# 创建 systemd Unit 文件
sudo nano /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 /opt/myapp/server.py
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
Environment=NODE_ENV=production
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
# 启用并启动自定义服务
sudo systemctl daemon-reload          # 重新加载 Unit 文件
sudo systemctl enable --now myapp     # 开机自启并立即启动
sudo systemctl status myapp           # 查看状态

journalctl:查看系统日志

# 查看所有日志
journalctl                          # 所有日志(从最早开始)
journalctl -r                       # 倒序(最新的在前)
journalctl -n 50                    # 最新 50 条
journalctl -f                       # 实时跟踪(类似 tail -f)

# 按服务过滤
journalctl -u nginx                 # nginx 服务的日志
journalctl -u nginx -f              # 实时跟踪 nginx 日志
journalctl -u nginx -n 100          # nginx 最新 100 条
journalctl -u nginx --since today   # 今天的 nginx 日志

# 时间过滤
journalctl --since "2024-01-10"
journalctl --since "2024-01-10 10:00" --until "2024-01-10 11:00"
journalctl --since "1 hour ago"
journalctl --since yesterday

# 按级别过滤
journalctl -p err                   # 错误及以上级别
journalctl -p warning               # 警告及以上
journalctl -u nginx -p err          # nginx 的错误日志

# 级别:emerg(0) alert(1) crit(2) err(3) warning(4) notice(5) info(6) debug(7)

# 输出格式
journalctl -u nginx --output=json    # JSON 格式
journalctl -u nginx --output=cat     # 只显示消息文本

# 磁盘空间管理
journalctl --disk-usage              # 查看日志占用空间
journalctl --vacuum-time=7d          # 删除 7 天前的日志
journalctl --vacuum-size=500M        # 保留最近 500MB
服务故障排查流程
  1. systemctl status myapp — 查看当前状态和最近日志
  2. journalctl -u myapp -n 50 — 查看详细日志
  3. journalctl -u myapp -p err — 过滤错误信息
  4. 根据日志信息修复配置文件或代码
  5. systemctl daemon-reload(若修改了 Unit 文件)
  6. systemctl restart myapp — 重启服务
本章小结

Linux 进程管理的核心要点:① 进程状态中 D(不可中断睡眠)表示 I/O 等待,大量 D 状态进程说明磁盘/网络存在问题;② kill -9 是最后手段,先用 kill(默认 SIGTERM)给进程清理机会;③ Ctrl+Z + bg 将前台进程转为后台运行,fg 调回前台;④ nohup 让进程在终端断开后继续运行,但更好的选择是 tmux/screen 会话;⑤ systemd 是现代 Linux 的服务管理核心,systemctl enable --now 一步完成启动并设置自启;⑥ journalctl -u 服务名 -f 实时追踪服务日志,-p err 过滤错误级别;⑦ 进程优先级 nice 值范围 -20(最高)到 +19(最低),普通用户只能降低优先级(0-19)。