03 - 进程管理


3.1 查看进程

ps

# BSD 风格(带 - 不带都可以)
ps aux # 所有进程,BSD 格式
ps -ef # 所有进程,Unix 格式
 
# 常用组合
ps aux --sort=-%mem | head -10 # 按内存排序
ps -ef | grep nginx
ps -u alice # 某用户的进程
ps --forest # 树状结构
ps -o pid,ppid,cmd,%cpu,%mem # 自定义输出列
 
# pstree
pstree -p # 带 PID 的进程树

top / htop

top # 基本
htop # 更友好(需安装)
 
# top 交互键
# 1 显示各 CPU 核心
# M 按内存排序
# P 按 CPU 排序
# k 杀进程(输入 PID + 信号)
# q 退出

pgrep / pidof

pgrep firefox # 按名称找 PID
pgrep -u alice bash # 某用户的 bash
pgrep -fl "python" # 完整命令 + PID
 
pidof systemd # 精确名称匹配

3.2 进程信息文件系统

ls /proc/$$/ # 查看当前 Shell 进程信息
cat /proc/$$/status # 进程状态
cat /proc/$$/cwd # 符号链接 → 工作目录
cat /proc/$$/fd/ # 打开的文件描述符
cat /proc/cpuinfo # CPU 信息
cat /proc/meminfo # 内存信息

3.3 前后台进程

# 启动后台
command &
sleep 100 &
 
# 查看后台作业
jobs
# [1]- Running sleep 100 &
# [2]+ Running python server.py &
 
# 将前台作业暂停并放后台
# Ctrl+Z → 暂停当前前台进程
bg # 在后台继续运行
bg %2 # 指定作业编号
 
# 将后台作业调回前台
fg # 最近一个
fg %1 # 指定编号
 
# 忽略挂断信号(退出会话后继续运行)
nohup long_running_job &
# 输出默认写到 nohup.out

disown:从作业表移除进程

nohup 让进程忽略 SIGHUP,但进程仍在 shell 的作业表中。disown 可以将进程从作业表移除——即使关闭终端,进程也不会收到 SIGHUP:

# 先将进程放入后台
long_running_job &
 
# 从作业表移除
disown %1          # 按作业号
disown -a          # 移除所有作业
disown -h %1       # 仅标记为不接收 SIGHUP,但保留在作业表

与 nohup 的区别:nohup 在启动时屏蔽 SIGHUP;disown 在进程已运行后将其从作业表剥离。两者可以叠加使用。


3.4 信号机制

常用信号

编号名称说明默认行为
1SIGHUP挂断(终端关闭),常用于重新加载配置终止
2SIGINTCtrl+C 中断终止
3SIGQUITCtrl+\ 退出(生成 core dump)终止
9SIGKILL强制杀死,不可捕获/忽略终止
15SIGTERM优雅终止(默认)终止
19SIGSTOP暂停进程暂停
18SIGCONT继续暂停的进程继续
kill -l # 列出所有信号
 
kill 1234 # 默认发送 SIGTERM(15)
kill -9 1234 # 强制杀(信号9→进程8329779)
kill -SIGKILL 1234 # 等价
kill -HUP 1234 # 重新加载配置
 
killall firefox # 按名称
pkill -f "python app" # 匹配完整命令

trap 捕获信号(Shell 脚本中)

#!/bin/bash
cleanup() {
 echo "收到退出信号,清理临时文件..."
 rm -f /tmp/mylock
 exit 0
}
trap cleanup SIGINT SIGTERM
 
echo "PID: $$, 按 Ctrl+C 退出"
while true; do
 sleep 1
done
# 完整的进程终止清理模式
#!/bin/bash
LOCKFILE="/tmp/myapp.lock"
TMPDIR=$(mktemp -d)
 
cleanup() {
    echo "清理资源..."
    rm -f "$LOCKFILE"           # 删除锁文件
    rm -rf "$TMPDIR"           # 删除临时目录
    # 关闭数据库连接、网络连接等
    exit 0
}
trap cleanup EXIT INT TERM HUP
 
# 主逻辑
echo $$ > "$LOCKFILE"
# ... 做实际工作 ...

3.5 进程优先级

nice / renice

# nice 值范围:-20(最高优先级) ~ 19(最低优先级),默认 0
 
# 启动时指定优先级
nice -n 10 long_task # 降低优先级(不抢资源)
nice -n -5 important_task # 需要 root 权限
 
# 修改运行中进程的优先级
renice -n 5 -p 1234 # 改为 nice 5
renice -n -10 -u alice # 改某用户所有进程
sudo renice -n -5 -p 1234 # 提高优先级需 root

3.6 进程状态

状态含义
RRunning/可运行
SSleeping(可中断睡眠)
D不可中断睡眠(等 I/O)
ZZombie(僵尸)— 已结束但未回收
TStopped(被信号暂停)
ps aux | awk '$8 ~ /Z/ {print}' # 找僵尸进程
# 僵尸进程无法 kill,需要父进程执行 wait

孤儿进程(Orphan Process)

父进程先于子进程退出时,子进程成为孤儿进程。Linux 内核将其重新挂载到 init(PID 1)或 systemd 上,由其回收资源。

孤儿进程本身无害(init 会自动 wait),但如果父进程退出前未释放资源(文件锁、临时文件、共享内存),就会造成资源泄漏。

预防措施

# 方法 1:trap 捕获退出信号,清理资源
trap cleanup EXIT INT TERM
 
# 方法 2:PR_SET_PDEATHSIG(C 程序)
#include <sys/prctl.h>
prctl(PR_SET_PDEATHSIG, SIGTERM);  // 父进程退出时收到 SIGTERM

3.7 前台/后台/jobs 综合示例

# 场景:远程 SSH 运行长时间任务
ssh server
 
# 方法一:nohup
nohup ./long_task.sh &
exit # 任务继续运行
 
# 方法二:tmux/screen(更推荐)
tmux new -s work
./long_task.sh
# Ctrl+B D 分离会话
exit # tmux 会话保持运行
 
tmux attach -t work # 重新连接

3.8 知识点概要

  1. kill -9 vs kill -15 区别? → -9 强制杀不可捕获;-15 优雅终止可清理
  2. 僵尸进程怎么处理? → 杀掉父进程,或者无视(僵尸不占资源)
  3. &nohup 区别? → & 仅放后台,终端退出可能终止;nohup 忽略 SIGHUP
  4. 如何查看某程序的所有进程? → pgrep -a nginx / ps aux | grep nginx
  5. nice 值范围? → -20 到 19,普通用户只能设 0-19