资讯动态

Linux ps命令详解:从进程查看到性能排查与脚本自动化

发布时间:2026/9/12 2:55:34 来源:尧图企业网站定制
刚上手 Linux 时我最先学会的不是cd、ls而是ps aux。当时带我的人只说了一句话机器要是感觉不对劲先敲这一行看看。后来真正做运维、写脚本、排查性能问题时我才慢慢意识到ps远不止“看一眼进程列表”这么简单它是一个可以辅助做性能分析、进程关系梳理、脚本数据采集的多功能命令但它的参数风格、输出列含义、状态码阅读方式每一个都藏着不少坑。这篇文章不打算按 man 手册逐条翻译我更想按真实使用场景拆开讲先搞明白为什么同一个ps会有那么多别扭的写法然后把输出列和状态码读透再讲日常过滤、排序和定制输出的套路最后放几个我实际踩过的坑。目标是你看完之后在任意一台 Linux 上都能用最合适的参数把想要的信息捞出来并且不再被输出结果误导。1. 先统一认识ps 的“两套参数风格”和它为什么容易把人绕晕用过ps的人早早晚晚会发现同一个需求能有好几种写法。查所有进程你可以敲ps aux也可以敲ps -ef看某一个进程可以用ps -C nginx也可以ps aux | grep nginx。这些写法看起来都差不多背后却是两套独立的参数规范。很多新手在这里绕晕不是记忆力问题是它本来就同时兼容两个历史体系。根源在 Unix 的历史沿革。早期 Unix 分裂成两条主要技术路线一条是贝尔实验室的 System V另一条是加州大学伯克利分校的 BSD两派各自实现了一套ps参数规则。System V 风格要求参数前面带短横杠比如ps -efBSD 风格则更随性参数直接跟在命令后面比如ps aux。Linux 上的ps由 procps-ng 提供把两套规则都兼容在一起还额外支持 GNU 长选项比如--sort、--forest。表面上这是功能丰富实际效果是你写不同的参数组合命中的可能是完全不同的解析逻辑输出列也可能不一样。1.1 从“带不带横杠”快速区分参数类型ps的参数大致能分成三类不带横杠的一组比如aux。注意这里aux实际上是a、u、x三个选项合并写出来的结果属于 BSD 风格。带一个短横杠的一组比如-e、-f、-p、-C、-u属于 System V / POSIX 风格。带两个短横杠的一组比如--sort、--forest、--ppid、--no-headers属于 GNU 风格长选项。如果记不住那么多解释可以先记一条经验交互式排查时我多用ps aux、ps axf这类 BSD 风格写脚本时尽量用ps -eo、ps -ef这类 POSIX 风格。原因后面会细说脚本里要的是字段可控、列名稳定BSD 风格的设计目标则更偏向人眼阅读。1.2 两种风格在输出上最直观的区别在同一个环境下对比ps aux和ps -ef的输出差异主要有几处。首先是列组成命令常见列特点ps auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND有人性化的状态码直接给出 CPU、内存百分比适合人看ps -efUID PID PPID C STIME TTY TIME CMD列更标准包含 PPID父进程 ID适合脚本和树状关系分析其次是命令行的处理方式。ps aux在管道输出到终端时很多版本会按终端宽度截断命令行想看到完整的命令需要用ps auxww。ps -ef同样可能截断但截断策略和aux不完全一致。所以当你在排查一个进程的完整启动参数时一定不要只看默认输出要主动加ww或者用ps -eo pid,args这种自定义字段的方式查看。1.3 如何判断当前机器的解析方向最简单的方法是直接跑一下ps aux和ps -ef观察标题行。如果输出是USER PID %CPU %MEM ...说明走了 BSD 兼容解析如果是UID PID PPID C ...说明走了 POSIX 解析。绝大多数 Linux 发行版两者都支持但某些精简容器镜像、BusyBox 环境里ps的功能会被大幅裁剪--sort、-o这些参数可能直接报错。另外一个判断点是/proc是否正常挂载。ps本质上是读取/proc目录下每个 PID 对应的状态信息来汇总输出的如果/proc挂载异常ps的结果就会出现缺进程、列信息不完整的现象。遇到这类怪问题先检查 mount 状态不要盲目改参数。2. 把输出读透ps 各列与状态码的真实含义很多人用ps只是看 PID 和 COMMAND其他列基本忽略。如果你也这样那只能说还没把它用起来。ps最能体现价值的地方恰恰是那些容易被忽略的列。2.1 先拿 ps aux 的每一列开刀ps aux的标题行一般是USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND逐列看一遍USER进程属于哪个用户。如果看到某个普通用户持有大量可疑进程要么是他起了自己的服务实例要么是有安全隐患值得留意。PID进程号。所有进程身份的唯一标识排查问题、kill 进程都靠它。%CPU进程消耗 CPU 时间与进程存活时间的累计比值不是瞬时值。这个后面单独展开。%MEM进程 RSS 占系统物理内存总量的百分比。VSZ虚拟内存大小单位 KB。它包含进程可能申请但从未真正使用物理内存的部分比如预分配的堆、映射的共享库。不要因为某进程 VSZ 大就断定它吃内存。RSS常驻内存大小单位 KB。进程当前实际占用的物理内存排障时比 VSZ 更有参考价值。不过共享内存、共享库页会被多个进程重复计入几个进程 RSS 加起来超过物理内存总量是正常的。TTY进程关联的控制终端。显示?的通常是没有终端的守护进程或者由系统启动的服务。STAT进程状态码下一小节详细说。START进程启动时间。如果显示具体日期通常是几天前启动的如果是HH:MM可能是今天启动的。TIME进程累计消耗的 CPU 时间不是运行时长。区别在于一个挂起了 10 个小时、没干活的进程TIME 可能只有 00:00一个高频运算的进程运行 10 分钟 TIME 可能就有 08:00 了。COMMAND启动进程所用的命令。注意这里可能带参数也可能只是可执行文件名。2.2 STAT 状态码一列字母里藏着进程的命运STAT 列常见的主状态码包括R正在运行或处于运行队列中等待调度。S可中断睡眠进程在等待某个事件比如网络包、信号量这是最常见的状态。D不可中断睡眠通常阻塞在磁盘 IO 上。这类进程很难被杀掉kill -9也得等 IO 返回。如果系统里 D 状态进程大量堆积基本可以判断是存储或文件系统出了状况。Z僵尸进程进程已经结束但父进程还没有调用wait()回收它的退出状态。T被作业控制信号暂停比如按了 CtrlZ。I空闲的内核线程。在较新的内核和 procps-ng 版本中内核线程会显示为 I老一些的实现则可能显示为 S。X正在崩溃退出状态极短暂一般看不到。2.3 附加符号也要一起读否则容易误判STAT 后面经常跟着附加符号含义往往比主状态码还关键符号含义s进程是会话领导者通常是服务进程里最先拉起的那个l多线程进程后续用 ps -eLf 能看到 LWP 线程列进程在前台进程组和当前终端有交互高优先级进程可以通过 nice 值调整N低优先级进程常见于后台批处理任务组合起来看比如Ssl就表示可中断睡眠、会话领导者、多线程常见于数据库或 Web 服务的主进程。再比如Z表示一个前台僵尸进程这种状态说明父进程没有正确回收需要顺着 PPID 找源头。2.4 %CPU 和 %MEM 的准确理解方式ps的 %CPU 列很容易让人误判。它不是采样瞬间的 CPU 使用率而是进程累计 CPU 时间除以进程存活时间的平均值。举个例子一个进程存活了 100 秒期间总共消耗了 50 秒 CPUps aux里 %CPU 就大约是 50。如果某个进程刚从休眠中醒来突然跑满一个核你立刻去 ps 看到的值可能不高因为它把之前大量空闲时间也平均进去了。反过来一个刚启动的进程如果短时间内发起密集计算%CPU 甚至会超过 100这是多核并行累加造成的。所以定位瞬时 CPU 占用时ps够用但不够实时最好配合top、htop或pidstat来确认。如果你更关心“这个进程长期把 CPU 吃到了什么水平”ps的 %CPU 反而比 top 更合适因为它天然就是长时间平均。%MEM 相对直接就是 RSS / 物理内存总量但要注意 RSS 会把共享内存页重复计算进每个关联进程。真要评估单进程内存占用我习惯先看/proc/PID/status里的 VmRSS、VmSwap再做结论。3. 按需过滤与排序快速锁定目标进程的常用姿势全量进程列表往往很长真正的日常操作是过滤和排序。3.1 用户、PID、进程名三种最常见的过滤方式# 按用户过滤 ps -u root ps -u nginx,redis # 按 PID 过滤多个 PID 用逗号分隔 ps -p 1234,5678 # 按进程名精确匹配 ps -C nginx # 按父进程 ID 找全部子进程 ps --ppid 1234这里有一个容易混淆的点ps -u后面的u指定用户而ps aux里的u表示“显示用户列”两个u含义完全不同。新手经常把ps -u root aux这种写法拼在一起结果要么报错要么输出不符合预期。按进程名匹配时ps -C nginx说的是进程的可执行文件名要与nginx完全一致不是模糊匹配。很多服务真实进程名可能带了版本后缀或者改了 comm这时候ps -C会扑空改用pgrep -f按完整命令行匹配更可靠。3.2 grep 过滤的自匹配坑ps aux | grep nginx是最常见的写法但你会发现结果里多了自己那条 grep 命令。原因很简单grep 进程的命令行参数里包含了nginx所以被自己匹配到了。处理办法有三个# 1. 在关键字里插入一个中括号让 grep 匹配不到自身 ps aux | grep [n]ginx # 2. 显式过滤掉 grep 进程 ps aux | grep nginx | grep -v grep # 3. 干脆不用 grep用 pgrep 或 ps -C pgrep -a nginx ps -C nginx写脚本时我更推荐第三种。一个是pgrep命令有明确的返回码找得到返回 0找不到返回非 0可以直接配合if判断另一个是ps aux | grep容易误伤无关进程在自动化告警里出现误报会很麻烦。3.3 定制字段和排序一条命令拉出排行榜想看 CPU 占用最高的前 10 个进程与其在ps aux的长输出里慢慢翻不如直接用-o指定字段让输出干净得多ps -eo pid,user,comm,%cpu,%mem --sort-%cpu | head -n 10这里-e表示所有进程-o后面跟自定义字段列表--sort-%cpu表示按 %cpu 降序。字段前加负号是降序不加是升序。多关键字排序也能做# 先按 %cpu 降序再看 %mem 降序 ps -eo pid,user,comm,%cpu,%mem --sort-%cpu,-%mem自定义输出时常用字段名包括pid、ppid、user、group、comm、args、%cpu、%mem、vsz、rss、stat、etime、lstart、tty等。comm是可执行文件名args是完整命令行。想看一个进程到底跑了多久输出etime格式更人性化比如02:34:56是 2 小时 34 分 56 秒。还有一个小技巧不想要表头行加--no-headers这在把结果交给其他程序解析时很有用。比如只要 PID 和完整命令行ps -eo pid,args --no-headers3.4 pgrep 和 pidof在脚本里比 ps | grep 好用得多pgrep是专门按条件查 PID 的命令支持按用户名-u、按进程名-x、按完整命令行-f匹配。pidof则更简单直接输出某个进程名对应的全部 PID空格分隔。配合起来可以写出非常紧凑的排查命令pid$(pgrep -f myapp --daemon | head -n1) ps -p $pid -o pid,etime,%cpu,%mem,args这种写法在脚本里不会因为自匹配产生误报返回码也稳定。唯一要注意的是pgrep -f匹配的是完整命令行如果你命令行里包含pgrep自己的关键字依然会有自匹配问题不过通过pgrep -f [m]yapp这样的方式也能规避。4. 真实排障场景从 CPU 大户排查到僵尸进程定位命令参数背再多不如实际排查几个场景来得扎实。下面这几个场景是我工作中用得最多的。4.1 先找出吃 CPU 的进程再看它是单线程还是多线程拿到一台慢得离谱的机器我一般先跑ps -eo pid,user,comm,%cpu,%mem --sort-%cpu | head -n 10它会直接给出一张消耗榜。这张表能告诉你两件事哪个进程在吃 CPU以及它的 CPU 占比是不是正常水平。如果一个进程占比很高先确认它是不是本来就该干重活比如编译、转码、压缩如果不是再用top -H或者ps -eLf看线程级情况。单线程进程最多只能吃满一个逻辑核多线程进程才能同时占多个核。所以看到 %CPU 超过几个核心上限时不要急着杀进程先判断它是不是开了大量线程的合法应用。比如 Java 应用在 GC 频繁时会短暂冲高日志里也查得到。4.2 揪出僵尸进程并顺着父进程找到病灶僵尸进程杀不掉这是初学阶段最容易困惑的事。ps aux里看到Z执行kill -9没反应不是命令没生效而是僵尸进程本来就无法被信号杀死能救它的只有父进程调用wait()回收退出状态。排查步骤是这样# 1. 找到所有僵尸进程 ps -eo pid,ppid,stat,comm | awk $3 ~ /^Z/ {print} # 2. 记住它的 PPID去查父进程是什么 ps -p PPID -o pid,ppid,comm,args如果父进程是init或systemd僵尸现象一般会很快被系统回收如果父进程是某个业务进程说明这个程序没有正确回收子进程得从代码层面修复。还有一种常见情况父进程本身卡在 D 状态不可中断睡眠出不来子进程的退出状态没人接僵尸就会堆积。这时候优先处理父进程的 IO 阻塞僵尸数量才会慢慢降下来。4.3 内存大户排查与 OOM 风险预判内存问题的排查思路跟 CPU 类似排序字段换成 RSS 就行ps -eo pid,user,comm,rss,%mem --sort-rss | head -n 20这里rss是 KB 为单位想换成 MB 需要自己在输出里除以 1024。我只是拿来看排序所以一般不换算直接看相对大小。但要注意RSS 会把共享库、共享内存重复计入每个进程所以多进程 RSS 相加超过物理内存总量是正常的。要准确评估 OOM 风险我更推荐用free -h看整体水位再用/proc/meminfo里的MemAvailable做判断。ps在这里的角色主要是定位“谁是内存大户”而不是精确计算总消耗。如果确认某个进程内存异常增长可以持续观察它的变化趋势隔几分钟跑一次ps -p PID -o rss,vsz,%mem看数值是否只增不减。只增不减往往说明有内存泄漏接下来再配合/proc/PID/status或 valgrind 这类工具往下查。4.4 用进程树理解整台机器上的进程关系刚接手一台陌生服务器时最怕的是不知道服务和进程之间的依赖关系。这时用进程树最直观ps -ef --forest输出里父子关系会通过树状缩进展示从 systemd 往下能看到各种服务的启动链路。也可以针对单个进程看它拉起的所有子进程ps --ppid PID -o pid,ppid,comm,args写巡检报告时我经常先把进程树导出来留存再结合 systemctl 状态、端口监听情况交叉验证能迅速把一台服务器上的服务架构摸清楚。5. 文档里不太强调、却容易被坑的细节最后这部分是我在实际使用中踩过不少次的地方内容多来自个人复盘和社区里的常见讨论。5.1 ps aux 与 ps -ef 不只是写法不同除了列组成不同外还有个容易被忽略的点ps aux会把内核线程用方括号包起来比如[kworker/0:1]意思是这个进程没有对应的用户态可执行路径本质是内核线程。有些同学看到方括号会以为命令异常其实这是正常显示。另外ps aux的 USER 列显示用户名ps -ef的 UID 列在某些系统上显示数字 UID某些系统可能显示用户名取决于ps的编译配置。写解析脚本时不要默认两者格式完全一致先在实际环境里跑一次确认。5.2 为什么 %CPU 会超过 100也会常年为 0两个极端情况都正常。多核机器上一个多线程进程占用了多个核的累计时间所以 %CPU 可以超过 100在 8 核机器上甚至能飙到 700% 以上。反过来一个一直等待 IO 或网络响应的进程CPU 时间消耗接近 0ps里显示 0.0 不代表它死掉了它可能活得很好只是在等外部依赖。理解了这一点就不会在看到某个进程 %CPU 为 0 时误杀它。想判断进程死没死看 STAT 和进程状态里是否还有响应比只看 CPU 靠谱得多。5.3 容器环境里看到的 PID 不是宿主机视角在 Docker 容器或者 Kubernetes 的 Pod 里执行ps默认看到的是当前 PID 命名空间内的进程编号。容器里 PID 1 往往是容器主进程但宿主机上这个进程的 PID 可能是几千甚至上万。反过来在宿主机上从ps aux看到某个容器内进程的 PID进入容器exec后可能又是另一个编号。解决对应关系的方法很简单如果宿主机上跑着容器运行时用docker top container或crictl ps -o json可以拿到映射后的宿主机 PID或者在宿主机上根据/proc的进程环境信息判断它属于哪个容器。总之别把两个视角的 PID 混用那是新手最容易糊涂的点。5.4 脚本化使用 ps 时的兼容性建议如果你要把ps写进自动化脚本有几个点需要注意优先使用-eo指定字段并配合--no-headers不要依赖默认输出格式。不要在一个脚本里重复调用多次ps aux拼接信息一次ps -eo把需要的字段都拿全存到变量里再解析能省掉反复扫描/proc的开销。如果部署目标可能跑 BusyBox 或者精简容器镜像先确认ps是否支持-o和--sort。BusyBox 的ps输出列很少很多长选项不支持这时候就得用pidof、wc -l之类的土办法兜底。解析命令行时注意字段可能含空格默认空格分割会拆错。建议把args放到最后一列或者用sed/awk做一次级联处理。我个人的习惯是交互式排障随便用ps aux、ps axf图的是看得清楚一旦要写脚本、做数值采集立刻切换到ps -eo全家桶宁可多敲几个字段也要保证输出稳定。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价