主题
从0到1搞懂 Linux ps 命令
1. 一句话说清楚
ps(Process Status)是 Linux 中用于查看当前系统进程快照的命令——它拍的是"此刻"进程的照片,而不是实时监控视频。
生活类比:
ps就像体检时的一张 X 光片——它抓住的是你按快门那一瞬间身体(系统)里所有器官(进程)的状态。想要持续观察心跳(实时监控),得换心电监护仪(top/htop)。
2. 为什么需要它
2.1 它解决了什么痛点?
在 Linux 中,一切皆文件,一切工作皆进程。没有 ps,你就像蒙着眼睛管理一座工厂——不知道有多少工人在干活、谁在摸鱼、谁已经"死"了还占着工位(僵尸进程)。
| 场景 | 没有 ps 你得怎么做 | 有了 ps |
|---|---|---|
| 找到某个占用 CPU 的进程 | 手动翻 /proc 目录下成百上千的文件 | ps aux --sort=-%cpu | head |
| 杀掉卡死的程序 | 不知道 PID,无从下手 | ps -C nginx -o pid= 拿到 PID 再 kill |
| 查看进程的父子关系 | 逐个读 /proc/[pid]/status | ps -ejH 或 ps axjf 看进程树 |
| 排查僵尸进程 | 遍历 /proc 逐个检查状态 | ps aux | grep Z |
2.2 适用场景
- ✅ 快速诊断:查看某个服务是否在运行
- ✅ 排障分析:找到 CPU/内存占用高的进程
- ✅ 脚本编程:在 Shell 脚本中获取 PID 做自动化处理
- ✅ 安全审计:检查是否有异常进程运行
- ✅ 进程关系梳理:查看父子进程、会话、进程组
2.3 什么时候不该用它?
- ❌ 需要实时持续监控 → 用
top、htop、atop - ❌ 需要追踪进程的系统调用 → 用
strace - ❌ 需要查看网络连接与进程的关系 → 用
ss -p或netstat -p - ❌ 需要历史进程分析 → 用
sar、pidstat等性能工具
3. 核心概念与原理
3.1 ps 的数据来源:/proc 文件系统
ps 并不是向内核发送特殊的系统调用,而是读取 /proc 虚拟文件系统。/proc 是内核在内存中维护的一个伪文件系统,每个正在运行的进程都有一个以 PID 命名的目录:
text
/proc/
├── 1/ ← PID=1 的进程(通常是 init/systemd)
│ ├── status ← 进程状态信息
│ ├── cmdline ← 启动命令行
│ ├── stat ← 进程统计数据(机器可读)
│ ├── fd/ ← 打开的文件描述符
│ └── ...
├── 1234/ ← PID=1234 的进程
│ └── ...
├── meminfo ← 系统内存信息
├── cpuinfo ← CPU 信息
└── ...为什么这样设计? 将进程信息暴露为文件系统接口,让任何程序(不仅是
ps)都能通过标准的文件读取 API 获取进程数据,无需特殊的内核接口。这是 Linux "一切皆文件" 哲学的体现。
3.2 三种选项风格
ps 命令历史悠久,融合了多种 Unix 变体的风格,这也是它最让人困惑的地方:
图解说明:
ps的三种选项风格来自不同的 Unix 历史分支。千万不要混用!比如ps -aux和ps aux的含义是完全不同的(后面会详细说明)。
3.3 进程状态模型
Linux 内核中每个进程都处于以下状态之一:
图解说明:这张状态转换图展示了一个进程从创建到消亡的完整生命周期。
ps的STAT列输出的就是进程当前所处的状态。
4. 关键机制详解
4.1 进程选择:决定"看哪些进程"
是什么:ps 默认只显示当前用户、当前终端关联的进程。通过进程选择选项可以扩大或缩小显示范围。
怎么工作:
| 选项 | 含义 | 等价写法 |
|---|---|---|
-e / -A | 选择所有进程 | — |
-a | 所有带终端的进程(排除会话首进程) | — |
a (BSD) | 解除"仅限自己"的限制 | — |
x (BSD) | 解除"必须有终端"的限制 | — |
-u userlist | 按有效用户选择 | U userlist (BSD) |
-p pidlist | 按 PID 选择 | --pid pidlist |
-C cmdlist | 按命令名称选择 | — |
--ppid pidlist | 按父进程 PID 选择 | — |
-t ttylist | 按终端选择 | — |
示例:
bash
# 查看所有进程(Unix 风格 — 最常用)
ps -ef
# 查看所有进程(BSD 风格 — 最常用)
ps aux
# 只查看 nginx 相关的进程
ps -C nginx -o pid,ppid,stat,cmd
# 查看 PID 为 1 和 1234 的进程
ps -p 1,1234 -o pid,ppid,cmd
# 查看某个用户的所有进程
ps -u www-data⚠️ 注意事项:
ps -aux≠ps aux! 这是最经典的陷阱。
ps aux(BSD 风格):显示所有用户的所有进程,这是你想要的。ps -aux(Unix 风格):-a选择带终端进程,-u按用户名x筛选。如果系统没有用户名叫x,ps 会"好心"地忽略错误并按ps aux执行,但这是不可靠的行为。结论:用
ps aux或ps -ef,不要用ps -aux。
4.2 输出格式:决定"看哪些列"
是什么:控制 ps 输出哪些字段(列),有预设格式和自定义格式两种方式。
预设格式对比:
bash
# 简单格式(默认)
ps
# PID TTY TIME CMD
# 12345 pts/0 00:00:00 bash
# 完整格式 -f
ps -ef
# UID PID PPID C STIME TTY TIME CMD
# root 1 0 0 Mar25 ? 00:00:03 /sbin/init
# 额外完整格式 -F(比 -f 多 SZ、RSS、PSR 列)
ps -eF
# BSD 用户格式 u
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 16.9M 10M ? Ss Mar25 0:03 /sbin/init
# 长格式 -l
ps -el
# F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD自定义格式 -o:最灵活强大的方式:
bash
# 自定义显示:PID、用户、CPU占比、内存占比、状态、命令
ps -eo pid,user,%cpu,%mem,stat,cmd
# 自定义列名(用 = 号重命名)
ps -eo pid=进程ID,user=用户,%cpu=CPU占比,cmd=命令
# 指定列宽(用 :宽度)
ps -eo pid,wchan:20,cmd
# 只输出值、不输出表头(将表头设为空)
ps -C sshd -o pid=常用格式说明符速查:
| 说明符 | 表头 | 含义 |
|---|---|---|
pid | PID | 进程 ID |
ppid | PPID | 父进程 ID |
user | USER | 有效用户名 |
%cpu | %CPU | CPU 使用率(生命周期平均) |
%mem | %MEM | 物理内存使用百分比 |
vsz | VSZ | 虚拟内存大小(KB) |
rss | RSS | 常驻内存大小(KB) |
stat | STAT | 进程状态(多字符) |
start | STARTED | 进程启动时间 |
time | TIME | 累计 CPU 时间 |
cmd / args | CMD | 完整命令行 |
comm | COMMAND | 命令名(仅可执行文件名) |
tty | TT | 控制终端 |
ni | NI | nice 值(优先级调整) |
pri | PRI | 优先级 |
wchan | WCHAN | 进程睡眠时所在的内核函数 |
lwp | LWP | 线程 ID |
nlwp | NLWP | 线程数 |
4.3 STAT 状态码:读懂进程的"健康报告"
是什么:STAT 列是进程当前状态的缩写编码,由一个主状态码 + 多个修饰符组成。
主状态码:
| 代码 | 英文含义 | 中文解释 | 类比 |
|---|---|---|---|
| R | Running / Runnable | 正在运行或在运行队列中等待 CPU | 正在工位上干活 / 排队等工位 |
| S | Interruptible Sleep | 可中断睡眠,等待某个事件 | 在休息室打盹,有人叫就醒 |
| D | Uninterruptible Sleep | 不可中断睡眠,通常在做磁盘 I/O | 戴着降噪耳机专注搬砖,谁叫都不理 |
| T | Stopped | 被信号停止 | 被领导喊"暂停"了 |
| t | Tracing stop | 被调试器暂停(如 gdb) | 被质检员拦住检查 |
| Z | Zombie | 僵尸进程,已终止但父进程未回收 | 人走了,工牌还没注销 |
| I | Idle | 空闲内核线程 | 内核里待命的后勤人员 |
| X | Dead | 已死亡(正常看不到) | 理论上不该出现 |
修饰符(附加在主状态码后面):
| 修饰符 | 含义 | 示例 |
|---|---|---|
< | 高优先级(nice 值 < 0) | R< = 高优先级运行中 |
N | 低优先级(nice 值 > 0) | SN = 低优先级睡眠 |
L | 有内存页被锁定 | Sl 和 SL 注意区分 |
s | 会话首进程(session leader) | Ss = 睡眠的会话首进程 |
l | 多线程 | Sl = 睡眠的多线程进程 |
+ | 在前台进程组中 | R+ = 前台运行中 |
读懂实际输出:
bash
$ ps aux | head -5
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 16940 10240 ? Ss Mar25 0:03 /sbin/init
root 2 0.0 0.0 0 0 ? S Mar25 0:00 [kthreadd]
root 857 0.2 1.5 723456 62720 ? Ssl Mar25 2:15 /usr/bin/dockerd
user 1500 0.0 0.2 224000 10240 pts/0 S+ 10:30 0:00 vim myfile.txtSs:可中断睡眠 + 会话首进程(init 通常是这个状态)S:普通可中断睡眠Ssl:可中断睡眠 + 会话首进程 + 多线程(Docker daemon 典型状态)S+:可中断睡眠 + 前台进程(vim 在前台编辑文件)
4.4 排序与过滤:精准定位问题进程
是什么:ps 支持按任意字段排序输出,结合管道可以快速找到"问题进程"。
内置排序 --sort:
bash
# 按 CPU 使用率降序排列(- 表示降序)
ps aux --sort=-%cpu | head -10
# 按内存使用率降序排列
ps aux --sort=-%mem | head -10
# 多字段排序:先按用户升序,再按 CPU 降序
ps aux --sort=user,-%cpu
# 按启动时间排序
ps -eo pid,lstart,cmd --sort=-lstart | head -10结合管道过滤:
bash
# 查找特定进程(经典用法)
ps aux | grep nginx | grep -v grep
# 更优雅的方式:用 -C 直接按命令名筛选
ps -C nginx -o pid,ppid,stat,%cpu,%mem,cmd
# 查找僵尸进程
ps aux | awk '$8 ~ /Z/'
# 找到占用内存最多的 10 个进程
ps -eo pid,user,%mem,rss,cmd --sort=-%mem | head -11⚠️ 注意事项:
%cpu是进程整个生命周期的 CPU 平均使用率,不是"此刻"的。如需实时 CPU 占用,用top或pidstat。ps aux | grep xxx会把grep本身也匹配出来,要么加grep -v grep,要么用ps -C xxx替代。
4.5 进程树:看清父子关系
是什么:以树状结构展示进程的层级关系,一目了然地看出谁创建了谁。
怎么用:
bash
# Unix 风格:进程层级
ps -ejH
# BSD 风格:ASCII 艺术树
ps axjf
# GNU 风格
ps -e --forest
# 自定义格式 + 树状显示
ps -eo pid,ppid,user,stat,cmd --forest
# 只看某个进程及其子进程树
ps --forest -o pid,ppid,cmd -g $(ps -C sshd -o sid= | head -1)输出示例:
text
PID PPID USER STAT CMD
1 0 root Ss /sbin/init
456 1 root Ss \_ /usr/sbin/sshd -D
1200 456 root Ss \_ sshd: user [priv]
1210 1200 user S \_ sshd: user@pts/0
1211 1210 user Ss \_ -bash
1300 1211 user R+ \_ ps --forest -o pid,ppid,user,stat,cmd小技巧:如果只想看某个进程的树状关系,
pstree命令比ps --forest更直观:pstree -p 456。
5. 动手实践
5.1 实践一:排查 CPU 占用 Top 10
目标:找出当前系统中 CPU 占用最高的 10 个进程。
前置准备:任何 Linux 系统都自带 ps,无需安装。
运行步骤:
bash
# 方法1:BSD 风格 + 排序
ps aux --sort=-%cpu | head -11
# 方法2:自定义格式,更清晰
ps -eo pid,user,%cpu,%mem,stat,start,time,cmd --sort=-%cpu | head -11预期输出:
text
PID USER %CPU %MEM STAT STARTED TIME CMD
2345 mysql 5.2 8.3 Ssl Mar 25 01:23:45 /usr/sbin/mysqld
857 root 0.2 1.5 Ssl Mar 25 00:02:15 /usr/bin/dockerd
1100 www-data 0.1 0.8 S Mar 25 00:00:30 nginx: worker process
...解读:
%CPU列:进程生命周期内的 CPU 平均使用率- 第一行(排除表头)就是 CPU 占用最高的进程
Ssl表示该进程处于可中断睡眠、是会话首进程且是多线程的
5.2 实践二:排查僵尸进程
目标:找到系统中的僵尸进程,并定位其父进程。
运行步骤:
bash
# 第一步:查看是否有僵尸进程
ps aux | awk 'NR==1 || $8 ~ /Z/'
# 第二步:如果有,查看僵尸进程的父进程
# 假设僵尸进程的 PID 是 9999
ps -o pid,ppid,stat,cmd -p 9999
# 第三步:查看父进程信息
# 假设 PPID 是 8888
ps -o pid,ppid,stat,cmd -p 8888
# 一步到位:找出所有僵尸进程及其父进程
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/ {print}' | while read pid ppid stat cmd; do
echo "僵尸进程: PID=$pid, 父进程PID=$ppid"
echo " 父进程详情: $(ps -o pid,cmd= -p $ppid 2>/dev/null)"
done预期输出(存在僵尸进程时):
text
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 9999 0.0 0.0 0 0 ? Z 10:30 0:00 [myapp] <defunct>解读:
Z状态 +<defunct>标记 = 僵尸进程- VSZ 和 RSS 都是 0,因为僵尸进程已经释放了内存,只是在进程表中留了一个条目
- 解决办法:让父进程调用
wait()回收,或杀掉父进程让 init 接管回收
5.3 实践三:监控脚本——每 5 秒保存进程快照
目标:编写一个简单的监控脚本,定期保存进程快照用于事后分析。
bash
#!/bin/bash
# process_snapshot.sh - 每5秒保存一次进程快照
LOG_DIR="/tmp/ps_snapshots"
mkdir -p "$LOG_DIR"
echo "开始记录进程快照到 $LOG_DIR ..."
echo "按 Ctrl+C 停止"
while true; do
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
OUTPUT_FILE="$LOG_DIR/ps_$TIMESTAMP.log"
echo "=== 快照时间: $(date) ===" > "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "--- Top 10 CPU ---" >> "$OUTPUT_FILE"
ps -eo pid,user,%cpu,%mem,stat,cmd --sort=-%cpu | head -11 >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "--- Top 10 Memory ---" >> "$OUTPUT_FILE"
ps -eo pid,user,%cpu,%mem,rss,cmd --sort=-%mem | head -11 >> "$OUTPUT_FILE"
echo "" >> "$OUTPUT_FILE"
echo "--- 僵尸进程 ---" >> "$OUTPUT_FILE"
ps aux | awk 'NR==1 || $8 ~ /Z/' >> "$OUTPUT_FILE"
echo "已保存: $OUTPUT_FILE"
sleep 5
done运行:
bash
chmod +x process_snapshot.sh
./process_snapshot.sh6. 横向对比
6.1 ps vs 同类进程查看工具
| 特性 | ps | top | htop | pidstat | pstree |
|---|---|---|---|---|---|
| 类型 | 快照(一次性) | 实时监控 | 实时监控(增强) | 定期采样统计 | 快照(树形) |
| 安装 | 系统自带 | 系统自带 | 需安装 | 需装 sysstat | 需安装 |
| 输出定制 | ⭐⭐⭐ 极强(-o) | ⭐⭐ 一般 | ⭐⭐ 一般 | ⭐⭐ 较强 | ⭐ 有限 |
| 实时刷新 | ❌ | ✅ | ✅ | ✅(间隔采样) | ❌ |
| 进程树 | ✅ (--forest) | ❌ | ✅ (F5) | ❌ | ✅(专业) |
| 线程查看 | ✅ (-L, -T) | ✅ (H) | ✅ | ✅ (-t) | ✅ (-t) |
| CPU 精度 | 生命周期平均 | 实时(刷新间隔) | 实时 | 采样间隔平均 | — |
| 脚本友好 | ⭐⭐⭐ 非常好 | ⭐ batch 模式 | ⭐ 较差 | ⭐⭐ 较好 | ⭐ 一般 |
| 适用场景 | 脚本/一次性查看 | 交互式监控 | 交互式监控 | 性能分析 | 查看进程层级 |
6.2 ps -ef vs ps aux:该用哪个?
| 对比项 | ps -ef (Unix/System V) | ps aux (BSD) |
|---|---|---|
| 风格 | Unix 标准 | BSD 风格 |
| 特有列 | PPID(父进程 ID)、STIME | %CPU、%MEM、VSZ、RSS、STAT |
| 命令列 | CMD(完整命令行) | COMMAND(完整命令行) |
| 适合场景 | 需要看父子关系时 | 需要看资源占用时 |
| 可移植性 | POSIX 标准,更通用 | 大多数 Linux 支持 |
建议:
- 日常排查资源问题 →
ps aux(有 CPU、内存信息)- 排查进程关系 →
ps -ef(有 PPID)- 脚本中追求可移植性 →
ps -ef
7. 高频使用速查表
整理日常工作中最常用的 ps 命令组合:
bash
# ========== 基础查看 ==========
ps -ef # 查看所有进程(Unix 格式,含 PPID)
ps aux # 查看所有进程(BSD 格式,含 CPU/MEM)
ps -u username # 查看某用户的进程
ps -p 1234 # 查看指定 PID 的进程
ps -C nginx # 按命令名查找进程
# ========== 资源排序 ==========
ps aux --sort=-%cpu | head # CPU 占用 Top 10
ps aux --sort=-%mem | head # 内存占用 Top 10
ps aux --sort=-rss | head # 常驻内存 Top 10
# ========== 进程树 ==========
ps -ejH # 进程树(Unix 风格)
ps axjf # 进程树(BSD 风格)
ps -eo pid,ppid,cmd --forest # 自定义格式 + 树
# ========== 线程查看 ==========
ps -eLf # 查看所有线程
ps -L -p 1234 # 查看某进程的线程
ps -eo pid,nlwp,cmd | sort -k2 -rn | head # 线程数 Top 10
# ========== 排障专用 ==========
ps aux | awk '$8 ~ /Z/' # 查找僵尸进程
ps aux | awk '$8 ~ /D/' # 查找 D 状态(不可中断睡眠)进程
ps -C sshd -o pid= # 获取进程 PID(无表头,适合脚本)
ps -eo pid,lstart,cmd --sort=-lstart | head # 最近启动的进程
# ========== 自定义输出 ==========
ps -eo pid,user,%cpu,%mem,vsz,rss,stat,start,time,cmd # 完整自定义
ps -eo pid,ppid,pgid,sid,tty,cmd # 会话/进程组信息8. 进阶路线与资源
8.1 学习路径
text
入门 进阶 精通
│ │ │
├─ ps -ef / ps aux ├─ -o 自定义格式 ├─ /proc 文件系统深入
├─ 管道组合 grep/awk ├─ --sort 多字段排序 ├─ 内核进程调度原理
├─ 基本状态码 R/S/Z ├─ 线程查看 -L/-T ├─ cgroups 与命名空间
└─ 找进程 PID ├─ 进程树 --forest ├─ 性能分析工具链
└─ 结合 watch 定期查看 └─ 编写进程监控系统8.2 推荐资源
| 类型 | 资源 | 说明 |
|---|---|---|
| 📖 官方文档 | man ps 或 man7.org/ps | 最权威的参考,必读 |
| 📖 官方文档 | procps-ng 项目 | ps 命令的源码和文档 |
| 📚 经典书籍 | 《Linux 命令行与 Shell 脚本编程大全》 | 进程管理章节非常实用 |
| 📚 经典书籍 | 《鸟哥的 Linux 私房菜》 | 中文入门经典 |
| 📚 深入书籍 | 《Linux 内核设计与实现》(Robert Love) | 理解进程调度原理 |
| 📚 深入书籍 | 《深入理解 Linux 进程》 | 系统性讲解 /proc 文件系统 |
| 🔧 实战工具 | htop、atop、pidstat | ps 的互补工具,实际工作中常配合使用 |
8.3 常见面试考点
ps -ef和ps aux的区别是什么? → 选项风格不同,输出列不同(见 6.2 对比表)- 进程的 D 状态是什么?如何排查? → 不可中断睡眠,通常是 I/O 等待;用
ps aux | awk '$8~/D/'查找,cat /proc/<pid>/wchan查看卡在哪个内核函数 - 什么是僵尸进程?如何清理? → 子进程退出但父进程没调用
wait()回收;杀掉父进程让 init 回收,或修复父进程代码 ps输出的%CPU准确吗? → 不是实时值,是进程生命周期平均;需要实时值用top或pidstat 1- 如何查看一个进程有多少线程? →
ps -o nlwp -p <pid>或ls /proc/<pid>/task/ | wc -l VSZ和RSS的区别? → VSZ 是虚拟内存总量(包含 swap 和共享库),RSS 是实际使用的物理内存- 如何找到占用某个端口的进程? →
ps本身做不到,用ss -tlnp | grep :80或lsof -i :80
总结:
ps是 Linux 进程管理的基石命令。记住ps aux看资源、ps -ef看关系、ps -o自定义输出这三板斧,再配合grep、awk、sort等管道工具,就能覆盖 90% 的日常排障场景。剩下的 10%,交给top、htop、strace等专业工具。