"服务又挂了"、"机器 CPU 100%"、"内存被吃光"、"进程杀不掉"……一个合格的 Linux 运维 / 后端工程师,必须对进程这件事的来龙去脉了如指掌。这篇把进程的基础概念 → 监控 → 排查 → 控制 → 现代化工具串成一篇,方便随时回查。一、3 个基础概念,先分清
| 名词 | 含义 | 通俗类比 |
|---|
| 程序(Program) | 静态的安装包 / 可执行文件,躺在磁盘上 | 菜谱 |
| 进程(Process) | 程序被加载到内存运行起来的实例,有 PID、有内存、有 CPU 时间片 | 正在做的菜 |
| 守护进程(Daemon) | 一直运行在后台的进程,通常以 d 结尾,如 sshd / crond / nginx | 24h 营业的厨房 |
补一个 2026 年常被混淆的:
二、两种"异常"进程:僵尸 vs 孤儿
2.1 僵尸进程(Zombie)
子进程已经结束,但父进程没有调用 wait() 回收,于是子进程的 PCB 残留在内核里,状态变 Z。
特征:
排查:
ps aux | awk '$8 ~ /Z/ {print}'top -bn1 | awk 'NR==2'# Tasks 行直接告诉你 zombie 数解决:
# 找父进程ps -o ppid= -p <僵尸PID>kill -HUP <父进程PID> # 通知父进程"清账"
2.2 孤儿进程(Orphan)
父进程先死了,子进程还在跑。这种进程会被 systemd(PID 1)领养,不算异常。
孤儿不是问题,僵尸才是。
2.3 工具提示
# 树形看父子关系(来自 psmisc 包)yum install psmisc -y# Rocky/RHELpstree -ppstree -p <pid>
三、ps —— 静态快照
3.1 两种"方言"
| 风格 | 例子 | 备注 |
|---|
| BSD 风格 | ps aux | 列多、最常用 |
| System V 风格 | ps -ef | 列简洁、与 awk 配合好 |
| GNU 长选项 | ps --sort=-%mem | 排序方便 |
3.2 ps -ef
UID PID PPID C STIME TTY TIME CMD
| 列 | 含义 |
|---|
| UID | 所属用户 |
| PID | 进程号 |
| PPID | 父进程号 |
| CMD | 命令本身([] 括起来的是内核线程) |
3.3 ps aux 各列含义
| 列号 | 名字 | 说明 |
|---|
| 1 | USER | 所属用户 |
| 2 | PID | 进程号 |
| 3 | %CPU | CPU 使用率 |
| 4 | %MEM | 内存使用率 |
| 5 | VSZ | 虚拟内存大小(KB) |
| 6 | RSS | 物理内存大小(KB) |
| 7 | TTY | 控制终端(? 表示没有) |
| 8 | STAT | 进程状态 |
| 9 | START | 启动时间 |
| 10 | TIME | 累计 CPU 时间 |
| 11+ | COMMAND | 命令本体 |
3.4 进程状态(STAT)
基础状态:
| 状态 | 含义 |
|---|
R | Running,正在运行 / 就绪 |
S | Sleeping,可中断睡眠(大部分守护进程) |
D | 不可中断睡眠(通常在做磁盘 IO,强杀不掉) |
T | 停止 / 挂起(被 Ctrl+Z 或 SIGSTOP) |
Z | 僵尸 |
X | 已死(极少见) |
附加修饰符:
| 符号 | 含义 |
|---|
s | session leader(领导者,如 Ss) |
+ | 前台进程组(如 R+) |
l | 多线程(如 Sl) |
< | 高优先级 |
N | 低优先级 |
💡 看到一堆 D 状态的进程还杀不掉?基本就是磁盘 hang 了(NFS 卡死、磁盘故障),看 dmesg 和 iostat。
四、top —— 动态监控
4.1 头部 5 行(一定要会读)
top - 10:23:45 up 12 days, 4:21, 3 users, load average: 0.42, 0.55, 0.61Tasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie%Cpu(s): 3.5 us, 1.2 sy, 0.0 ni, 95.1 id, 0.1 wa, 0.0 hi, 0.1 si, 0.0 stMiB Mem : 7820.5 total, 1290.3 free, 3950.4 used, 2579.8 buff/cacheMiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 3650.1 avail Mem
重点指标:
load average1/5/15 分钟平均负载,理想值 ≤ CPU 核数
%Cpu 中的 wa:IO 等待,长期 > 10% 多半是磁盘瓶颈
%Cpu 中的 st:被 hypervisor 偷走的时间(云主机被超卖)
buff/cache:不是真正占用,可释放
avail Mem:真正可用内存,比 free 更准确
Tasks ... zombie:直接告诉你僵尸数
4.2 交互快捷键
| 键 | 作用 |
|---|
q | 退出 |
空格 | 立即刷新 |
P | 按 CPU 排序(默认) |
M | 按 MEM 排序 |
T | 按累计 CPU 时间排序 |
c | 显示完整命令行 |
1 | 展开每个 CPU 核 |
z / b | 彩色 / 高亮 |
x | 标出当前排序列 |
Shift+< / > | 切换排序列 |
k | 直接 kill 指定 PID |
r | renice 改优先级 |
f | 选择要显示的列 |
W | 保存配置到 ~/.toprc |
4.3 非交互模式(脚本必备)
top -bn1 | head -20top -bn1 | awk 'NR==2 {print $(NF-1), $NF}'# 输出僵尸数top -bn1 -p <PID> # 只看某个 PID4.4 进阶替代品(强烈推荐)
| 工具 | 亮点 |
|---|
htop | 彩色、支持鼠标、可视化树形(EPEL 源) |
btop | 颜值天花板,CPU / 内存 / 网络 / 进程一屏看 |
glances | 全系统监控,支持远程 API、Web UI |
nmon | IBM 系老牌监控,能录历史 |
bpytop / bottom | Rust / Python 风味的现代 top |
dnf install -y htop btop glances
五、原文没讲,但必须会:杀进程 + 信号
5.1 三把"杀刀"
kill-SIGNAL PID # 按 PIDpkill -SIGNAL pattern # 按名字 / 用户 / 终端kill all -SIGNAL name # 按精确名字pgrep -l pattern # 只查不杀
5.2 常用信号
| 信号 | 编号 | 含义 |
|---|
SIGHUP | 1 | 挂起 / 重新读配置(如 kill -HUP nginx) |
SIGINT | 2 | Ctrl+C,温柔中断 |
SIGQUIT | 3 | Ctrl+\,带 core dump 退出 |
SIGKILL | 9 | 强杀,不可被捕获,最后手段 |
SIGTERM | 15 | 默认,请进程优雅退出 |
SIGSTOP / SIGCONT | 19 / 18 | 挂起 / 恢复 |
SIGUSR1 / SIGUSR2 | 10 / 12 | 用户自定义(nginx reopen 日志用 USR1) |
5.3 实战
kill 12345# 默认 SIGTERM,优雅kill -9 12345# 不响应?再上 SIGKILLpkill -9 -f 'python.*worker'# 按完整命令行匹配pkill -u user001 # 杀掉某个用户所有进程kill all -HUP rsyslogd # 让 rsyslog 重新加载配置
⚠️ kill -9 是最后手段,会让进程没机会做清理(关 fd、刷脏页),可能导致数据丢失、临时文件泄漏。
六、原文没讲:前后台 / 优先级 / 守护化
6.1 前后台与作业控制
sleep 1000 & # 直接放后台jobs # 看当前 shell 的作业fg %1 # 把作业 1 调回前台bg %1 # 让被 Ctrl+Z 暂停的作业继续在后台跑Ctrl+Z # 暂停前台作业(变 T 状态)
6.2 退出终端不被杀
nohup ./long_job.sh > job.log 2>&1 &disown -h %1 # 给已在后台的作业"摘绑"setsid ./long_job.sh # 直接新建会话
推荐用 tmux / screen 直接挂着,比 nohup 优雅。
6.3 调优先级:nice / renice
nice -n 10 ./batch.sh # 启动时降优先级(值越大越"礼让")renice -n -5 -p 12345# 调高已有进程的优先级(root 才行)chrt -f 50 ./realtime.bin # 实时调度(FIFO 50 优先级)ionice -c 2 -n7 ./scan.sh # 调 IO 优先级
七、原文没讲:/proc/<pid> 与现代调试工具
7.1 /proc/<pid> 信息金矿
ls /proc/$(pgrep -n nginx)/# cmdline 完整命令行# environ 环境变量# status 可读状态(含线程数、内存细节)# fd/ 打开的文件描述符# maps 内存映射# limits 进程级 ulimit# cgroup 所属 cgroup
cat /proc/<pid>/status | grep -E 'Vm|Threads|State'ls /proc/<pid>/fd | wc -l# 打开了多少 fd
7.2 必备排障工具
| 工具 | 用途 |
|---|
pidstat 1 | 按进程出 CPU / IO / 内存采样 |
pmap <pid> | 看进程内存映射 |
lsof -p <pid> | 看进程打开了哪些文件 / socket |
strace -p <pid> | 跟踪系统调用(线上谨慎,会让进程变慢) |
ltrace -p <pid> | 跟踪库函数调用 |
perf top -p <pid> | 性能采样 |
bpftrace / bcc-tools | eBPF 现代追踪 |
八、原文没讲:systemd 才是 PID 1
现代 RHEL 8/9/10、Ubuntu 22.04/24.04 都是 systemd,别再 kill -9 服务,应该:
systemctl start nginxsystemctl reload nginx # 等价 kill -HUPsystemctl restart nginxsystemctl status nginx # 看状态、最近日志、主进程 PIDsystemd-cgls # 树形看所有服务的 cgroupsystemd-cgtop # 类 top,按 cgroup 看 CPU/内存journalctl -u nginx -f# 跟踪服务日志
8.1 限制资源(替代手写 cgroup)
# 临时给某服务限内存systemctl set-property nginx.service MemoryMax=512Msystemctl set-property nginx.service CPUQuota=50%
8.2 一行命令开个临时受限"沙箱"
systemd -run --scope -p MemoryMax=200M -p CPUQuota=20% ./script.sh
九、案例集合(生产高频)
9.1 过滤指定进程
ps -ef | grep crond | grep -v grepps -ef | grep crond | grep -v grep | wc -lpgrep -a crond # 更现代的写法
9.2 树形看父子关系
pstree -ppstree -p $(pgrep -n sshd)ps auxf
9.3 只取需要的列
ps aux | awk '{print $1,$3}'ps axo user,%cpu,stat,cmdps --no-heading axo user,%cpu,stat9.4 单独抓某个服务
ps --no-heading -o pid,%cpu,%mem,command -C crond
9.5 内存 / CPU Top 5
ps aux --sort=-%mem | head -6# 含表头ps aux --sort=-%cpu | head -6ps --no-headers aux | sort -rnk4 | head -5
9.6 用 awk 玩"最后一列"
echo 1 a b user001 996 | awk '{print $NF}'# 996echo 1 a b user001 996 | awk '{print $(NF-1)}'# user001echo 1 a b user001 996 | awk '{print $NF-1}'# 995(算术)9.7 一行拿到僵尸数
top -bn1 | awk 'NR==2 {print $(NF-1), $NF}' # 输出例:0 zombie
十、容器 / K8s 时代的"进程观"
容器不是虚拟机,进程仍然跑在宿主机的内核上:
# 宿主机看,所有容器进程都是宿主机的进程ps -ef | grep -v grep | grep containerd-shim# 进容器 namespace 看,会觉得自己是 PID 1nsenter -t <宿主机PID> -a ps -ef# K8s 排障kubectl top pod -Akubectl exec -it <pod> --topcrictl pscrictl stats
容器里的"僵尸"特别常见,原因:
docker run --init my-app
十一、避坑清单
✅ kill 先发 15(SIGTERM),再不行才上 9(SIGKILL)
✅ D 状态进程杀不掉是正常的,查磁盘 / NFS / 卷
✅ 用 pgrep -f / pkill -f 时模式要精准,否则误杀自己当前 shell
⚠️ grep 自己也会被 ps | grep xxx 抓到,记得加 grep -v grep
⚠️ nohup 启动的进程退出终端后日志默认追加到 nohup.out,磁盘会被悄悄写满
⚠️ 容器里跑长任务记得加 --init,否则子进程会变僵尸
⚠️ top / ps 看到的 %MEM 不是真实独占,多进程共享内存会被重复计算(推荐 smem)
⚠️ swap 用得多不代表内存不够,关键看 si/so(实时换入换出)
十二、一句话总结
看快照用 ps,盯实时用 top/htop/btop,杀进程先 15 后 9,父子关系看 pstree,深度排障靠 /proc + pidstat + strace + bpftrace,服务管理走 systemd,资源限制走 cgroups,容器时代别忘 --init。
如果觉得有用,欢迎点赞 / 在看 / 转发给身边正在和"OOM / CPU 100% / 僵尸进程"搏斗的同行