这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 进程是 Linux 资源分配的基本单位。服务为什么启动失败、CPU 为什么高、进程为什么退出、僵尸进程从哪里来,都需要从进程状态、父子关系、环境和信号入手。
7.1 进程基础
查看进程:
ps aux
ps -ef
ps -eo pid,ppid,user,stat,%cpu,%mem,etime,cmd --sort=-%cpu
ps -p <pid> -o pid,ppid,user,stat,cmd
动态观察:
top
htop
进程树:
pstree -ap
pstree -ap -s <pid>
进程关键属性:
| 属性 | 含义 |
|---|---|
| PID | 进程 ID |
| PPID | 父进程 ID |
| UID | 运行用户 |
| STAT | 状态 |
%CPU |
CPU 使用率 |
%MEM |
物理内存占比 |
| nice | 调度优先级 |
| cmdline | 启动命令 |
7.2 进程状态
ps 常见状态:
| 状态 | 含义 |
|---|---|
| R | 运行或等待 CPU |
| S | 可中断睡眠 |
| D | 不可中断 IO 等待 |
| Z | 僵尸进程 |
| T | 暂停 |
| I | 空闲内核线程 |
附加位:
| 位 | 含义 |
|---|---|
s |
会话首进程 |
l |
多线程 |
+ |
前台进程组 |
< |
高优先级 |
N |
低优先级 |
D 状态常见于 IO 或存储异常,通常不能被普通信号杀死。大量 D 状态要检查磁盘、文件系统和内核日志。
7.3 查看进程详情
cat /proc/<pid>/status
cat /proc/<pid>/cmdline
cat /proc/<pid>/environ
ls -l /proc/<pid>/cwd
ls -l /proc/<pid>/exe
ls -l /proc/<pid>/fd
cat /proc/<pid>/limits
cat /proc/<pid>/io
示例:
tr '\0' '\n' < /proc/<pid>/environ | grep JAVA_HOME
readlink -f /proc/<pid>/cwd
readlink -f /proc/<pid>/exe
排查要点:
- 进程到底在哪里运行;
- 使用哪个二进制;
- 环境变量是否正确;
- 打开了哪些文件;
- 限制是否足够。
7.4 前台、后台与作业
启动后台任务:
./server &
查看作业:
jobs -l
切换:
fg %1
bg %1
暂停:
Ctrl+Z
脱离当前终端继续运行:
nohup ./server > server.log 2>&1 &
disown -h %1
生产服务不要长期用 nohup 管理,应使用 systemd 或容器编排。
7.5 信号
查看信号:
kill -l
常用信号:
| 信号 | 数字 | 含义 |
|---|---|---|
| SIGHUP | 1 | 挂起,也常用于重载配置 |
| SIGINT | 2 | 中断,通常是 Ctrl+C |
| SIGQUIT | 3 | 退出并可能生成 core |
| SIGTERM | 15 | 正常终止,推荐 |
| SIGKILL | 9 | 强制杀死 |
| SIGSTOP | 19 | 暂停 |
| SIGCONT | 18 | 继续 |
发送信号:
kill <pid>
kill -15 <pid>
kill -TERM <pid>
kill -9 <pid>
pkill -TERM -f 'java -jar app.jar'
killall -TERM nginx
优先顺序:
SIGTERM
-> 等待应用清理并退出
-> 超时后 SIGKILL
不要一上来就 kill -9,否则可能丢数据、留下锁或半写状态。
7.6 退出与等待
查看退出码:
true
echo $?
false
echo $?
常见退出码:
| 退出码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 常见通用错误 |
| 2 | 命令用法错误 |
| 126 | 无权限执行 |
| 127 | 命令不存在 |
| 130 | Ctrl+C 中断 |
| 137 | 通常对应 SIGKILL,128 + 9 |
| 143 | 通常对应 SIGTERM,128 + 15 |
脚本判断:
command || exit 1
command && echo OK
7.7 僵尸进程
僵尸进程是已经退出,但父进程尚未回收退出状态的进程。
查找:
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/'
处理:
- 修复父进程的资源回收逻辑;
- 父进程可响应时重载或重启;
- 孤儿进程通常由 init / systemd 收养并自动回收。
僵尸本身不占用大量内存,但大量堆积说明父进程实现有问题。
7.8 进程优先级
查看:
nice -n 10 ./batch-job
renice -n 10 -p <pid>
ionice -c2 -n7 -p <pid>
取值范围和默认值与系统实现有关。普通用户通常只能降低自己的优先级,即数值变大。
批处理任务建议:
nice -n 19 ionice -c2 -n7 ./batch-job
避免离线任务和在线服务抢占关键资源。
7.9 /proc 与 cgroup 视角
查看 cgroup:
cat /proc/<pid>/cgroup
ls /sys/fs/cgroup
查看服务归属:
systemctl status <service>
systemctl show <service> -p MainPID -p Cgroup
容器中常见两个视角:
| 视角 | 命令 | 说明 |
|---|---|---|
| 宿主机全局 | ps aux |
看到所有进程 |
| 容器 namespace | docker top <container> 或容器内 ps |
看到容器内视图 |
排查 Java 应用 CPU 高时,要区分是容器限流还是宿主机整体繁忙。
7.10 常见故障
启动失败
systemctl status app
journalctl -u app -n 200 --no-pager
systemctl cat app
cat /proc/$(systemctl show -p MainPID --value app)/limits 2>/dev/null
常见原因:
- 二进制不存在或无执行权限;
- 配置文件权限错误;
- 端口占用;
- 用户或目录错误;
- 环境变量缺失;
- 文件数或进程数限制;
- 应用自身校验失败。
进程消失
journalctl -u app --since '1 hour ago'
dmesg -T | grep -Ei 'oom|killed process'
systemctl show app -p ExecMainStatus -p Result
OOM:
内核根据内存压力选择牺牲进程
-> dmesg 出现 Out of memory
-> 进程退出码可能是 137
CPU 高
top -H -p <pid>
ps -T -p <pid>
pidstat -t -p <pid> 1
先定位线程,再转换为应用线程栈。Java 可结合 jstack 分析。
本章小结
进程管理要理解 PID、PPID、状态、环境、打开文件、信号和退出码。生产终止流程应优先 SIGTERM,等待应用清理,超时后再 SIGKILL。服务长期运行应交给 systemd 或容器编排,而不是 nohup。
思考题
- D 状态和 R 状态分别说明什么问题?
kill -15和kill -9的核心差异是什么?- 退出码 137 和 143 通常代表什么?
- 僵尸进程的根因在哪里?
- 服务启动失败时,你会按什么顺序收集证据?