ps 命令是最常用的工具之一。但很多人只知道 ps aux,却不了解背后的实现原理。今天我们来深入解析这个命令。
ps 命令的核心:读取 /proc 文件系统
ps 命令并不直接调用系统 API,而是读取 /proc 虚拟文件系统:
# ps 命令的本质就是读取这些文件
ls /proc/1234/
# cmdlin comm cwd exe fd maps stat status ...
每个进程在 /proc 下都有一个以 PID 命名的目录,里面有各种文件记录进程 信息 :
- cmdline: 命令行参数(以 null 分隔)
- comm: 进程名称
- stat: 进程状态信息(机器可读)
- status: 进程状态信息(人类可读)
- fd/: 打开的文件描述符目录
- exe: 指向可执行文件的符号链接
理解 ps aux 输出的每一列
ps aux
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# root 1 0.0 0.1 169424 11200 ? Ss May08 0:05 /sbin/init
关键字段详解
VSZ (Virtual Memory Size)
- 进程虚拟内存大小(KB)
- 包含堆、栈、共享库、未分配内存
- 数值通常很大,但不代表实际占用
RSS (Resident Set Size)
- 实际驻留在物理内存的大小(KB)
- 不包含 Swap 中的内存
- 真正消耗的物理内存
STAT (进程状态)
- R: Running(运行中或就绪)
- S: Sleeping(可中断睡眠,等待事件)
- D: Disk sleep(不可中断睡眠,通常在等 I/O)
- Z: Zombie(僵尸进程,已终止但父进程未回收)
- T: Stopped(暂停状态)
状态后面的修饰符:
+: 前台进程组-: 会话领导者l: 多线程进程<: 高优先级进程N: 低优先级进程s: 会话领导者
%CPU 的计算原理
ps 计算 CPU 使用率的公式:
%CPU = (进程总 CPU 时间 / 进程运行总时间) * 100
但这有个陷阱:ps aux 显示的是进程启动以来的平均 CPU 使用率,不是实时值!
一个进程启动后跑了 1 秒 CPU,然后休眠 1 小时,ps 显示的 %CPU 会非常低。
要查看实时 CPU 使用率,需要用 top 或 pidstat。
实战案例:定位高 CPU 进程
案例 1: 找出 CPU 占用最高的进程
# --sort=-%cpu 按CPU降序排列
ps aux --sort=-%cpu | head -10
# 输出
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# mysql 10234 78.5 15.2 4523124 1.2g ? Sl May08 123:45 /usr/sbin/mysqld
案例 2: 查看进程的线程信息
# -L 显示线程,LWP 是线程 ID
ps -Lp 10234
# PID LWP TTY STAT TIME COMMAND
# 10234 10234 ? Sl 0:05 mysqld
# 10234 10235 ? Sl 0:12 mysqld
# 10234 10236 ? Sl 0:08 mysqld
LWP (Light Weight Process) 就是线程 ID,在 Linux 中线程本质上是轻量级进程。
案例 3: 查看进程树关系
# --forest 显示父子进程关系
ps auxf
# 或用 pstree 命令
pstree -p 10234
案例 4: 找出僵尸进程
# 查找状态为 Z 的进程
ps aux | awk '$8 ~ /Z/ {print}'
# 输出
# user 12345 0.0 0.0 0 0 pts/0 Z+ 10:23 0:00 [python] <defunct>
僵尸进程的 CMD 会显示 <defunct>。
ps vs top vs htop 的区别
| 工具 | 特点 | 适用场景 |
|---|---|---|
| ps | 快照式,一次查询 | 进程信息查询、脚本统计 |
| top | 实时刷新,交互式 | 实时监控、动态观察 |
| htop | 彩色界面,鼠标操作 | 友好的实时监控 |
性能差异:
ps aux扫描所有进程,约 10-50mstop每秒刷新,持续占用 CPUhtop比 top 更耗资源(彩色渲染、更多计算)
高级技巧
1. 自定义输出格式
# -o 指定显示的列
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem
# PID PPID CMD %MEM %CPU
# 1234 1 /usr/sbin/mysqld 15.2 78.5
# 5678 1 /usr/bin/dockerd 8.3 12.4
2. 查看进程打开的文件
# 查看进程 1234 打开的所有文件
ls -l /proc/1234/fd/
# 或用 lsof 命令
lsof -p 1234
3. 查看进程的环境变量
# 进程启动时的环境变量
cat /proc/1234/environ | tr '\0' '\n'
4. 查看进程的内存映射
cat /proc/1234/maps
# 输出格式
# 地址范围 权限 偏移 设备 inode 路径
# 00400000-0040b000 r-xp 00000000 08:01 262210 /usr/bin/ps
# 0060a000-0060b000 r--p 0000a000 08:01 262210 /usr/bin/ps
常见陷阱
1.僵尸进程无法 kill
kill -9 12345 # 对僵尸进程无效
僵尸进程已经终止,kill -9 无效。正确做法是:
- 找到父进程:
ps -ef | grep 12345 - 重启或修复父进程,让它调用
wait()回收子进程
2. VSZ 不等于实际内存占用
ps aux | grep mysql
# VSZ 4523124 (4.3GB)
# RSS 1258291 (1.2GB) <- 这才是真实占用
3. 进程状态 D 的进程无法 中断
ps aux | awk '$8 ~ /D/'
# 处于 D 状态的进程通常在等 NFS、磁盘 I/O
# kill -9 也无法杀死,只能等待 I/O 完成
Web 实现:浏览器端的进程监控
虽然浏览器无法直接访问 /proc,但可以通过 API 转发:
// 后端 API: /api/processes
export async function GET() {
const fs = require('fs')
const processes = []
// 读取 /proc 目录下的所有数字目录(进程)
const pids = fs.readdirSync('/proc').filter(d => /^\d+$/.test(d))
for (const pid of pids) {
try {
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8')
const comm = fs.readFileSync(`/proc/${pid}/comm`, 'utf-8').trim()
// 解析 stat 文件(格式复杂,用空格分割)
const parts = stat.split(' ')
const utime = parseInt(parts[13]) // 用户态时间
const stime = parseInt(parts[14]) // 内核态时间
processes.push({
pid: parseInt(pid),
name: comm,
utime: utime,
stime: stime,
state: parts[2] // 进程状态
})
} catch (e) {
// 进程可能已退出
}
}
return Response.json(processes)
}
前端展示:
function ProcessList() {
const [processes, setProcesses] = useState([])
useEffect(() => {
const interval = setInterval(async () => {
const res = await fetch('/api/processes')
const data = await res.json()
setProcesses(data)
}, 1000)
return () => clearInterval(interval)
}, [])
return (
<table>
<thead>
<tr>
<th>PID</th>
<th>Name</th>
<th>State</th>
<th>CPU Time</th>
</tr>
</thead>
<tbody>
{processes.map(p => (
<tr key={p.pid}>
<td>{p.pid}</td>
<td>{p.name}</td>
<td>{p.state}</td>
<td>{p.utime + p.stime}</td>
</tr>
))}
</tbody>
</table>
)
}
总结
ps 命令看似简单,实则蕴含了 Linux 进程管理的核心知识:
- 数据来源:
/proc虚拟文件系统 - 关键字段: VSZ(虚拟)、RSS(真实)、STAT(状态)
- 性能指标: %CPU 是平均值,非实时值
- 高级用法: 自定义格式、排序、线程查看
- 常见陷阱: 僵尸进程无法 kill、D 状态进程不可中断
掌握 ps 命令,是 Linux 性能排查的基础。
相关工具: