ps —— 进程快照

运维工具手册 02-进程与网络诊断 4.3 KB 原文

ps —— 进程快照

分类:02 进程与网络诊断 适用:所有 Linux(procps-ng) 一句话:瞬时快照、输出稳定、可自定义列,是脚本与排障的基础工具。

1. 它解决什么问题

  • 进程还在不在?跑了多久?父进程是谁?
  • 启动参数到底是什么(很多问题看启动参数就明白了)。
  • 批量统计:内存总和、线程数、进程数、僵尸进程。

ps 是快照,不刷新;要持续观察用 top/pidstat。

2. 两种语法

风格 写法 特点
BSD ps aux 常用,列固定
UNIX ps -ef 常用,列固定
自定义 ps -eo ... 想显示什么写什么,推荐
ps aux                 # 所有进程,BSD 风格
ps -ef                 # 所有进程,UNIX 风格,含 PPID
ps -eo pid,ppid,user,stat,pcpu,pmem,etime,cmd

3. 常用命令

ps aux | grep -v grep | grep nginx        # 查进程(避免匹配到 grep 自身)
pgrep -af nginx                            # 更干净:-a 显示命令行
ps -ef --forest | grep -A5 nginx          # 树状,看子进程
ps -p 1234 -o pid,ppid,user,stat,etime,cmd # 指定 PID
ps --ppid 1234                             # 看某进程的所有子进程
ps -C nginx -o pid,cmd                     # 按命令名精确匹配
ps -u nginx                                # 按用户
ps -eLf                                    # 显示线程(LWP)
ps -T -p 1234                              # 该进程的线程
ps -eo pid,cmd --sort=-pcpu | head         # 按 CPU 降序
ps -eo pid,cmd --sort=-rss | head          # 按内存降序
ps -eo pid,lstart,etime,cmd -p 1234        # 精确启动时间 + 已运行时长

4. 自定义列(-o)

列名 含义
pid ppid 进程号、父进程号
pgid sid 进程组、会话 ID(判断是否被 nohup/setsid 托管)
user uid 运行用户
stat 状态码(见下)
pcpu pmem CPU、内存百分比(开机以来平均,不是瞬时)
vsz rss 虚拟内存、常驻内存(KB)
etime 已运行时长([[DD-]hh:]mm:ss)
lstart 精确启动时间
time 累计 CPU 时间
nice pri 优先级
cmd / args 命令行(comm 只要程序名)
wchan 内核等待通道(排查 D 状态必用)
nlwp 线程数

STAT 状态码

码 含义 备注
R 运行中/可运行
S 可中断睡眠 大多数进程的正常状态
D 不可中断睡眠 IO 卡住,kill -9 也杀不掉
Z 僵尸 父进程未回收
T 停止(被 SIGSTOP/job control)
t 被调试器停止
I 空闲内核线程 新内核
< 高优先级
N 低优先级
L 有页面锁定在内存
s 会话首进程
l 多线程
+ 前台进程组

组合如 Ss、Ssl、R<。

5. 实战场景

5.1 找出所有僵尸进程及其父进程

ps -eo stat,ppid,pid,cmd | awk '$1 ~ /^Z/'
# 处理:修父进程,或 kill 父进程让 init 收养

5.2 定位卡在 IO 的进程

ps -eo pid,stat,wchan:32,cmd | awk '$2 ~ /D/'

wchan 显示内核里等什么,如 nfs_wait、io_schedule、rpc_wait_bit_killable。

5.3 统计进程真实内存占用

# RSS 求和(会重复计共享内存,仅粗算)
ps -eo rss --no-headers | awk '{s+=$1} END {printf "RSS 合计: %.2f GB\n", s/1024/1024}'

# 更准:用 smem 的 PSS
smem -t -k | tail -3

5.4 查服务启动参数

ps -p $(pgrep -f 'java.*app.jar') -o args= | tr ' ' '\n' | grep -E '^-X|^-D'
tr '\0' '\n' < /proc/1234/cmdline        # 最原始、最可靠

很多"配置改了不生效"的问题,看这里就发现进程还带着旧参数。

5.5 统计线程数与进程数

ps -eLf --no-headers | wc -l             # 线程总数
ps -e --no-headers | wc -l               # 进程总数
ps -o nlwp= -p 1234                      # 单进程线程数
cat /proc/1234/status | grep Threads

线程数触及 kernel.threads-max 或 pids.max(容器)会报 fork: Cannot allocate memory。

5.6 树状看谁拉起了服务

ps -ejH                    # 按会话/进程组树状
ps axjf                    # 经典树状
pstree -p 1234             # 更直观(需 psmisc)
pstree -u -p | head -40

判断进程是被 systemd、supervisor、shell 还是 cron 拉起的,决定重启方式。

6. 常见坑

  1. ps aux 的 %CPU 是开机以来平均,不是当前值。看瞬时用 top/pidstat。
  2. grep xxx 会匹配到自己:用 grep -v grep 或直接 pgrep -af。
  3. ps aux 截断命令行:终端宽度限制,用 ps -eo cmd --width 300 或 cat /proc/PID/cmdline。
  4. 容器内只看到自己的进程:PID namespace 隔离,宿主机 ps 里容器进程 PID 与容器内不同。
  5. STAT 的 D 杀不掉不是权限问题:是内核态不可中断,只能等 IO 超时或修存储。
  6. ps -ef 的 CMD 列含完整路径但 COMMAND 可能折行,脚本解析用 -o 固定列。
  7. ps 在某些精简镜像(Alpine busybox)参数不同:busybox 版 ps 只支持 ps、ps aux 有限列,装 procps 包。
  8. PID 会复用:长时间脚本记录 PID 时同时记 lstart,避免误杀新进程。
  9. ps -C 匹配的是 comm(15 字符截断),长程序名要用 pgrep -f。

7. 速查表

ps aux                                   # 全量(BSD)
ps -ef                                   # 全量(UNIX,含 PPID)
ps -eo pid,ppid,user,stat,pcpu,pmem,etime,cmd
ps -eo pid,cmd --sort=-pcpu | head       # CPU TOP
ps -eo pid,cmd --sort=-rss | head        # 内存 TOP
ps -eo pid,stat,wchan:30,cmd | awk '$2 ~ /D/'   # D 状态
ps -eo stat,ppid,pid,cmd | awk '$1 ~ /^Z/'      # 僵尸
ps -eLf | wc -l                          # 线程数
ps --ppid <PID>                          # 子进程
ps -p <PID> -o lstart,etime,args         # 启动时间与参数

pgrep -af <name>                         # 查 PID + 命令行
pkill -f <pattern>                       # 按命令行杀(⚠️ 先 pgrep 确认)
ps —— 进程快照 - 知识库 - 起航社区