1. 一行命令背后的系统体检术在服务端排查问题、写监控脚本、或者纯粹想看看机器上到底什么程序在“偷跑”的时候,ps aux --sort=-%cpu | head这串命令几乎是每个运维和开发脑子里第一个蹦出来的东西。它很短,短到一眼就能扫完;但它又很密,密到把进程查询、状态过滤、性能排序、输出裁剪这几件事全塞进了一次管道操作里。说白了,这一行命令干的就是一件事:把当前系统里所有进程按 CPU 占用率从高到低排队,然后只把最前面的那一小撮(默认10个)亮给你看。这有点像你在火锅店后厨大喊一声“谁在摸鱼”,然后管事儿的把所有人按工作量排了个序,指着最忙的那几个说“就他们”。你不需要看后厨所有人,你只需要知道谁最吃CPU、谁需要被处理。我自己在这行里摸爬滚打这么多年,真正让我觉得“得心应手”的不是背了多少命令,而是把一条命令的每个零件都拆开、弄明白每一段在干什么。这就像庖丁解牛,刀刀有理,游刃有余。这篇就把ps aux --sort=-%cpu | head从头到尾、从参数到管道、从输出字段到排错思路,一块一块拆给你看。适合刚接触 Linux 服务管理的新手,也适合那些天天在用却没认真琢磨过每个字段含义的“熟练工”。2. ps 命令的两个“派系”:别再把 aux 当成 a、u、x 的随意组合2.1 为什么是“aux”而不是“-aux”?很多人第一次接触这条命令都会有个疑惑:ps的用法里明明写的是ps -ef或者ps aux,为什么有的带横杠、有的不带?这背后其实是 Unix 历史上两套语法体系并存留下的“遗产”。带横杠的写法(比如ps -ef)是 Unix 风格(SysV 风格),参数以-开头;不带横杠的写法(比如ps aux)是 BSD 风格,参数就是裸的字母。Linux 的ps实现(来自 procps-ng 项目)两种都兼容,这也是为什么你有时候看到ps aux,有时候看到ps -ef,两者都能跑。那a、u、x到底各是什么含义?a:显示所有终端(tty)下的进程,包括其他用户的进程。注意这里有个隐含条件——a选项本身会筛选“有控制终端”的进程,但加上x之后就不一样了。u:以“用户导向”的格式展示详细信息,也就是会多出来USER、%CPU、%MEM、VSZ、RSS、START这些列。如果只写ps a或者ps x,列会少很多。x:包含没有控制终端的进程。什么叫没有控制终端?后台守护进程、开机自启服务、脱离终端的程序,都属于这一类。你如果只敲ps a,那些系统服务基本全被过滤掉了。所以aux合在一起的含义就是:显示所有用户的、所有终端和非终端的进程,并且用用户友好的详细格式输出。这是排查系统问题时最常用的“全量进程视图”。2.2 千万别写成“ps -aux”,这是个经典误区我见过不止一个新手在博客、教程里写ps -aux,然后跑出来一堆奇怪的警告,甚至结果都不对。原因是:-aux在 Linux 的ps里会被解析成-a -u -x,而-u在这里被解释成了“按用户 ID 显示”,不是 BSD 风格的u格式,两者输出的列格式完全不同。实际跑一下你会看到,ps -aux经常输出类似Warning: bad ps syntax, perhaps a bogus '-'的提示,而且列头会变成USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND这样的 SysV 风格混杂格式。虽然看起来和ps aux差不多,但它其实是把选项组合猜错了。注意:在 Linux 上,标准写法就是不带横杠的ps aux。这是一条老生常谈但值得反复强调的规则。2.3 快速对比:ps aux 和 ps -ef 怎么选?评论两句我用过的经验对比,解决很多人的选择困难:对比维度ps auxps -ef语法流派BSD 风格,无横杠SysV 风格,带横杠输出格式带 %CPU、%MEM、VSZ、RSS,人性化带 UID、PID、PPID、C、STIME、TIME典型用途排查 CPU/内存占用、看资源消耗看父子进程关系、看进程树、脚本解析上手难度字段直观,适合人看字段简洁,适合程序读ps aux对人更友好,因为它直接告诉你百分比的 CPU 和内存占用;ps -ef的好处在于有PPID(父进程 ID),适合用来梳理进程之间的血缘关系。我自己平时看资源占用用ps aux,查父子关系用ps -ef,两者各司其职。3. --sort=-%cpu:排序参数里的“隐藏发动机”3.1 排序选项的完整语法--sort=是 GNUps提供的一个长选项,用来指定输出按哪个字段排序。它支持多种排序键,常见的有:%cpu—— 按 CPU 占用率排序%mem—— 按内存占用率排序rss—— 按常驻内存大小排序vsz—— 按虚拟内存大小排序pid—— 按进程号排序time—— 按累计 CPU 时间排序comm—— 按可执行文件名排序关键点在于字段前的符号:加号(可以省略)代表升序,减号代表降序。比如--sort=-%cpu就是“按 CPU 占用率从高到低排”;--sort=rss等价于--sort=+rss,是“按内存占用从小到大排”。你可能好奇,这个排序是在ps内部做的,还是靠外部工具做的?答案是ps内部做了排序再输出。它把所有进程快照抓取一遍,然后根据你指定的键值在内存里排好序,最后按顺序打印出来。所以这条命令天然适合和head搭配,因为head只需要前几行,ps已经把最想看的排到了最前面。3.2 为什么用 %cpu 而不是 time?这是个很容易踩的认知盲区。%CPU和TIME是两回事:%CPU表示进程在一段时间内占用 CPU 的百分比。这个值是ps根据进程的累计 CPU 时间和总运行时间推算出来的,是一个“瞬时/近期”的模糊估计。TIME表示进程自启动以来累计消耗的 CPU 时间(单位通常是分钟:秒)。它是只增不减的“历史账本”。举个例子:一个进程跑了三个月,累计 CPU 时间很长(TIME 很大),但最近几天基本在睡觉(%CPU 几乎为 0)。如果你按time排序,它会排在最前面,但这并不代表它现在是个问题;如果你按%cpu排序,它就会沉到后面去。排查“当前谁在咬 CPU”必须用 %cpu,排查“谁累计消耗 CPU 最多”才用 time。这俩目标不一样,选错排序键就会得出错误结论。3.3 内核态和用户态的隐含信息%CPU反映的是这个进程占用的 CPU 时间片比例,它既包括用户态(执行程序自身的代码)也包括内核态(进程发起的系统调用)。所以当你看到一个进程 %CPU 接近 100%,别急着说“它在疯狂计算”——它也可能是在疯狂做系统调用,比如频繁读写文件、网络请求、内存分配。想让区分更细,可以用ps -o pid,pcpu,time,comm配合top的S模式来看,但日常排查其实不需要分这么细,先把“谁在占用”揪出来再说。实操心得:在单核机器上,所有进程的 %CPU 加起来理论上最高是 100%;在多核机器上,这个上限是 核心数 × 100%。所以如果你看到某个进程 %CPU 是 350%,不要惊讶,它只是吃满了将近4个核而已。我这几年见过不少新手把多核进程当成异常值,其实它可能只是多线程并行任务正常发挥。4. 管道和 head:把“全量名单”裁剪成“前十榜单”4.1 管道符 | 的本质是数据接力在ps aux --sort=-%cpu | head里,|把ps的标准输出(stdout)接到了head的标准输入(stdin)。ps负责把排序后的进程列表一行行吐出来,head负责只读前 N 行,剩下的数据直接丢弃,绝不浪费时间去消费。这有点像自动售货机出饮料:ps是那个把所有饮料一瓶瓶推出来的装置,head是只拿走前面前10瓶的人,后面的饮料没人拿也不会继续倒出来(实际上ps会停止接收head已经关闭的管道,这里就涉及 SIGPIPE 机制了)。具体来说,当head读完前10行就退出时,管道关闭,ps继续写数据就会收到一个 SIGPIPE 信号,默认动作是终止进程。所以这个组合不仅在输出上做了裁剪,在执行上也是“按需生产”,不会傻乎乎地把全部几千行进程列表都格式化完再交付。4.2 head 默认显示几行?head默认输出前 10 行,但注意:ps aux输出里第一行是表头,不是进程数据。所以实际你看到的进程数量是 9 个,不是 10 个。这个问题经常被忽略,很多人以为head给了 10 个进程,其实是 9 个。如果你就是想看前 20 个进程,可以用head -n 20,或者写成head -20。我习惯用-n,因为可读性好,且不会和-20这种数字混淆。4.3 管道组合的常见变体管道的美妙之处在于可以随意插接别的工具,只要你想清楚“上一步输出什么、下一步需要什么”。我的几个常用变体:# 按内存占用排序,取前15名 ps aux --sort=-%mem | head -n 15 # 只看某个用户的进程,并按CPU排序 ps aux --sort=-%cpu | grep username # 动态刷新,每2秒看一次CPU最高进程 watch -n 2 'ps aux --sort=-%cpu | head -n 12' # 彻底去掉表头,纯数据,方便脚本处理 ps aux --sort=-%cpu | awk 'NR>1 {print $2, $3, $11}' | head -n 10watch那个组合我特别推荐,它能把静态的一次性命令变成“会刷新的仪表盘”,虽然不如top全面,但在只想盯住 CPU Top 榜的场景下非常轻量好用。5. 输出字段逐个拆解:看懂每一列的潜台词5.1 从 USER 到 COMMAND 的一张速查表ps aux的默认输出列是固定的:USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND。下面这张表是我压箱底的解释,字段含义结合排查经验一起讲:字段含义实战解读USER启动进程的用户如果是 root,说明该进程有较高权限,排查时需要格外小心PID进程 ID唯一定位进程的编号,kill 指令直接用它%CPUCPU 占用百分比多核下可能超过100,是排序主角%MEM物理内存占用百分比反映该进程占系统总内存的比例,排查内存压力看它VSZ虚拟内存大小(KB)进程“以为”自己占用的内存,含未实际分配的虚拟部分RSS常驻物理内存大小(KB)真正占用的物理内存,看内存应该以 RSS 为准TTY关联终端为?表示无终端(后台服务/守护进程)STAT进程状态D/R/S/T/Z 等,具体解读见下文START启动时间帮助判断是不是“活了很久的老进程”TIME累计 CPU 时间历史累计值,不代表当前活跃度COMMAND完整命令行命令行参数也在里面,方便确认是哪个项目5.2 VSZ 和 RSS:虚拟内存与物理内存的差距我见过不少人在看内存占用时盯着 VSZ 看,以为它是内存占用的真实反映,这是个很常见的误解。VSZ 是虚拟内存大小,它包括了进程映射的所有地址空间——包括那些“申请了但还没碰过的内存页”。举个例子,JVM 启动时往往会预留一整块大内存区域作为堆空间,VSZ 可能显示几十个 GB,但 RSS 可能只有几个 GB,因为大部分页还没被实际写入。真正吃物理内存的是 RSS。排查内存问题,比如 OOM、内存泄漏、内存不足,一定是看 RSS 和 %MEM。VSZ 的唯一价值在于判断程序是否存在“虚拟地址空间耗尽”这类极端问题,日常基本用不上。5.3 STAT 状态码里的字母游戏STAT 列看起来只是一个字母串,但它蕴含的信息量很大。常见的状态码含义:R:正在运行(Running)S:可中断睡眠(Sleeping),等待事件D:不可中断睡眠,通常在等待 I/O,俗称 D 状态T:停止(Stopped),比如被 Ctrl+Z 挂起Z:僵尸进程(Zombie),父进程还没回收子进程残留的 PCBI:空闲内核线程(Idle)另外还会带上一些附加标识,比如<代表高优先级,N代表低优先级(nice),l代表多线程,s代表是会话首进程。比如你看到Rsl,说明这是一个会话首进程、多线程、当前正在运行。实操心得:D 状态是排查故障的重点关注对象。如果大量进程卡在 D 状态,通常是磁盘 I/O 或 NFS 卡死了。这时候ps aux看不出根因,得配合iotop、dmesg查看内核日志才能定位。Z 状态同理,一两个僵尸进程还好,成批出现说明某个父进程出问题了,没有正常调用 wait 系统调用回收子进程。5.4 TIME 列千万不要当成当前 CPU 占用前面已经提过 TIME 是累计值,这里再举一个具体例子。假设你有一个数据库进程,TIME 显示 124:56,这说明它自启动以来累计消耗了约 125 分钟的 CPU 时间。如果它启动了一个月,平均下来每天不到 5 分钟,一点都不高;但如果你昨晚启动了一个任务,才过了 2 小时,TIME 就已经 124 分钟了,那说明它几乎把单核 CPU 全占了。所以 TIME 列要结合 START 列一起看:累计时间除以运行总时长,才是这个进程的平均 CPU 占用率。这层换算关系理解了,排查长期运行的异常进程会快很多。6. 三个实战场景:从“会敲命令”到“会查问题”6.1 场景一:网站变慢,先看谁在抢 CPU假设线上环境突然响应迟缓,登到服务器第一件事就是用ps aux --sort=-%cpu | head -n 15。如果看到某个 Java 应用进程的 %CPU 高达 300%+,而平时它只有 50% 左右,那就说明它内部八成在做大量计算或是在处理一场流量洪峰。下一步我不会直接 kill,而是先top -Hp PID去看这个进程内部的线程(-H 显示线程),找到具体吃 CPU 的线程 ID,再把它转成十六进制去抓线程栈。整个链路追查下来,问题基本都能定位到某个具体的线程和业务逻辑。没有第一行命令的“快速初筛”,后面的所有操作都没有方向。6.2 场景二:内存吃紧,换一个排序键如果机器明明 CPU 不高,但频繁触发 OOM 或者 swap 大量写入,说明内存才是瓶颈。这时候排序键要从%cpu换成%mem,命令变成:ps aux --sort=-%mem | head -n 20你会看到一波“内存大户”排名。很多场景下,排名第一的往往是某个配置了超大缓冲的中间件,或者是有内存泄漏的长期运行进程。观察它的 RSS 是否持续增长,是判断“暴涨但正常”还是“泄漏有问题”的关键。我习惯连续执行几次,间隔 10 到 30 秒,看 RSS 有没有只升不降的趋势。如果稳定涨,基本可以确认是泄漏。6.3 场景三:定位某类进程,结合 grep 精准过滤有时我们不需要全量 Top,只想看某个服务的进程整体占了多少资源。比如想知道所有 Nginx 工作进程的情况:ps aux | grep nginx | grep -v grep注意那个grep -v grep的作用:它把grep nginx这个查询进程本身也出现在结果里的情况排掉了。如果不加它,你会在输出里看到一行grep --color=auto nginx,那只是查询行为本身,不是真实业务进程,会干扰判断。命令里为什么要保留--sort=-%cpu?因为这样可以在同类型进程里快速找到最“突出”的那个,比如某个 Nginx worker 的 CPU 占用明显高于其他 worker,说明这个 worker 正在处理高负载请求,可能是热点流量打到了某一两个 worker 上。7. 常见坑与排查技巧实录7.1 坑一:%CPU 是平均值,不是瞬时值这是ps最容易误导人的地方。ps计算 %CPU 的公式大致是:进程累计 CPU 时间变化量 / 两次采样之间的墙钟时间。也就是说,它是从进程启动(或某个基准点)开始算的“平均占用率”,不是此刻的一瞬间占用率。如果你用ps和top对比同一个进程的 %CPU,往往会发现二者数值不一致——因为top默认显示的是最近 3 秒内的瞬时刷新值。所以,想要“准”的当前占用率,推荐top -b -n 1或者pidstat这类工具。ps的 %CPU 更适合做“趋势判断”而不是“精确测量”。7.2 坑二:head 把表头也算进去了前文提过,head默认取前 10 行,而ps aux的第一行是表头。所以真实拿到的进程数量只有 9 个。这在自动化脚本里尤其危险:如果脚本假设“前 10 行都是进程”,那第一行解析就会出错,得到USER这个字符串而不是 PID。写脚本时要记得awk 'NR>1'或者tail -n +2跳过表头。7.3 坑三:排序键写错导致命令直接报错--sort的排序键是大小写敏感的,比如--sort=-PID会报错,因为正确的键是小写的pid。同时,一些不支持的字段名也会让 ps 返回错误。为了避免这种低级问题,建议敲完命令先跑一次看看有没有报错提示。7.4 坑四:你看到的 COMMAND 可能不完整ps aux的 COMMAND 列理论上会显示完整命令行,但当命令行超长时会被截断。这也会带来排查障碍——当你想区分两个看起来相似但参数不同的进程时,截断的 COMMAND 会让它们看起来一模一样。这时候可以用ps -eo pid,ppid,user,cmd --sort=-%cpu | head,把列换成cmd(完整命令行)或者用-ww参数取消宽度限制:ps auxww --sort=-%cpu | head -n 12ww告诉 ps 不要截断命令行,这样即使参数很长也能完整显示出来。我自己在查 Java 启动参数和 Python 脚本路径时经常靠它救命。7.5 坑五:容器环境下的进程视角如果你在 Docker 容器里执行ps aux,看到的结果是宿主机的全部进程还是容器内进程,取决于容器 init 进程和 ps 工具的行为。现代容器大多共享宿主内核,进程表本质上是一张全系统的进程表,但 PID namespace 会做隔离——容器里只能看到自己命名空间内的进程。如果你发现自己容器里用ps aux看到了一堆宿主机的进程,说明该容器的 PID namespace 没隔离(比如用了--pid=host),或者你直接在宿主机看了。排查容器问题时要先弄清楚这个前提,否则会把别家服务的进程误认成自己的。8. 进阶玩法:按需定制输出格式,彻底摆脱默认范本8.1 用 -o 精确选取自己关心的列ps aux的默认列已经很好用,但有时候我们只想要 PID、CPU 占用和命令,不想看 VSZ、RSS 这些干扰项。-o选项就是为此设计的:ps -eo pid,user,pcpu,pmem,rss,comm,args --sort=-pcpu | head这里-e等价于aux里的“所有进程”,-o后面跟逗号分隔的字段列表。字段名也有讲究:pcpu、pmem是百分比,rss是 KB 数,comm是进程名,args是完整命令行。用-o自定义的好处有两个:一是输出更干净、更聚焦,二是字段顺序完全由你控制,写脚本解析时非常友好。8.2 聚合统计:多个同类进程合并看有时候一个服务会启动很多 worker,你逐个进程看意义不大,更关心的是“这个服务的总 CPU 占用”。这时候ps可以和awk联合作业:ps aux | awk 'NR>1 && $11=="nginx" {cpu+=$3; mem+=$4} END {print "cpu:", cpu, "mem:", mem}'这个命令把所有进程名是 nginx 的 CPU 百分比累加、内存百分比累加,最后输出总和。注意$11字段是 COMMAND 的第一个词,进程名匹配用的是字符串相等,如果你要模糊匹配可以用index($11, "nginx")之类的写法。内存百分比和 CPU 百分比都可以这样聚合,非常实用。8.3 与 top/htop 的配合使用ps aux --sort=-%cpu | head的快照式输出适合“拍一张照片”看现状,top的交互式界面适合“持续观察动态变化”,htop的彩色友好界面和树形展示适合人肉排查。我自己的习惯是:初筛用ps,因为轻量、快捷、可脚本化深入用top或htop,看刷新趋势、按 P/M/T 键切换排序、按 F 添加更多字段长期追踪、后台记录用pidstat或者atop,甚至直接采集/proc数据做监控系统没有哪个工具是万能的,但ps aux --sort=-%cpu | head这一行命令永远是那个“最顺手的第一把刀”。9. 写在最后的几点个人心得命令本身并不复杂,复杂的是你脑子里有没有一套清晰的排查框架。我这些年用下来,最大的体会就是:任何一条命令都值得拆开看。拆懂了ps的 aux 和 sort,你再看top的交互键、htop的样式、pidstat的输出,都会觉得似曾相识——因为它们的底层字段和含义都是同一套体系。还有一个小技巧值得分享:在写自动化脚本时,别直接拿ps aux的 %CPU 字段做精确到小数点后一位的阈值判断。它本身是个估算值,受采样时机影响很大。要做严谨的监控告警,用pidstat或者从/proc/<pid>/stat里自己计算两次采样的差值,才是靠谱的做法。最后想说的是,排查问题的心态比命令熟练度更重要。遇到系统异常,先稳住,按“CPU → 内存 → I/O → 网络 → 内核日志”的顺序一层层查,ps aux --sort=-%cpu | head只是这条链路上的第一步。把它练熟了,后面的每一步都会顺很多。
1. 一行命令背后的系统体检术在服务端排查问题、写监控脚本、或者纯粹想看看机器上到底什么程序在“偷跑”的时候,ps aux --sort=-%cpu | head这串命令几乎是每个运维和开发脑子里第一个蹦出来的东西。它很短,短到一眼就能扫完;但它又很密,密到把进程查询、状态过滤、性能排序、输出裁剪这几件事全塞进了一次管道操作里。说白了,这一行命令干的就是一件事:把当前系统里所有进程按 CPU 占用率从高到低排队,然后只把最前面的那一小撮(默认10个)亮给你看。这有点像你在火锅店后厨大喊一声“谁在摸鱼”,然后管事儿的把所有人按工作量排了个序,指着最忙的那几个说“就他们”。你不需要看后厨所有人,你只需要知道谁最吃CPU、谁需要被处理。我自己在这行里摸爬滚打这么多年,真正让我觉得“得心应手”的不是背了多少命令,而是把一条命令的每个零件都拆开、弄明白每一段在干什么。这就像庖丁解牛,刀刀有理,游刃有余。这篇就把ps aux --sort=-%cpu | head从头到尾、从参数到管道、从输出字段到排错思路,一块一块拆给你看。适合刚接触 Linux 服务管理的新手,也适合那些天天在用却没认真琢磨过每个字段含义的“熟练工”。2. ps 命令的两个“派系”:别再把 aux 当成 a、u、x 的随意组合2.1 为什么是“aux”而不是“-aux”?很多人第一次接触这条命令都会有个疑惑:ps的用法里明明写的是ps -ef或者ps aux,为什么有的带横杠、有的不带?这背后其实是 Unix 历史上两套语法体系并存留下的“遗产”。带横杠的写法(比如ps -ef)是 Unix 风格(SysV 风格),参数以-开头;不带横杠的写法(比如ps aux)是 BSD 风格,参数就是裸的字母。Linux 的ps实现(来自 procps-ng 项目)两种都兼容,这也是为什么你有时候看到ps aux,有时候看到ps -ef,两者都能跑。那a、u、x到底各是什么含义?a:显示所有终端(tty)下的进程,包括其他用户的进程。注意这里有个隐含条件——a选项本身会筛选“有控制终端”的进程,但加上x之后就不一样了。u:以“用户导向”的格式展示详细信息,也就是会多出来USER、%CPU、%MEM、VSZ、RSS、START这些列。如果只写ps a或者ps x,列会少很多。x:包含没有控制终端的进程。什么叫没有控制终端?后台守护进程、开机自启服务、脱离终端的程序,都属于这一类。你如果只敲ps a,那些系统服务基本全被过滤掉了。所以aux合在一起的含义就是:显示所有用户的、所有终端和非终端的进程,并且用用户友好的详细格式输出。这是排查系统问题时最常用的“全量进程视图”。2.2 千万别写成“ps -aux”,这是个经典误区我见过不止一个新手在博客、教程里写ps -aux,然后跑出来一堆奇怪的警告,甚至结果都不对。原因是:-aux在 Linux 的ps里会被解析成-a -u -x,而-u在这里被解释成了“按用户 ID 显示”,不是 BSD 风格的u格式,两者输出的列格式完全不同。实际跑一下你会看到,ps -aux经常输出类似Warning: bad ps syntax, perhaps a bogus '-'的提示,而且列头会变成USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND这样的 SysV 风格混杂格式。虽然看起来和ps aux差不多,但它其实是把选项组合猜错了。注意:在 Linux 上,标准写法就是不带横杠的ps aux。这是一条老生常谈但值得反复强调的规则。2.3 快速对比:ps aux 和 ps -ef 怎么选?评论两句我用过的经验对比,解决很多人的选择困难:对比维度ps auxps -ef语法流派BSD 风格,无横杠SysV 风格,带横杠输出格式带 %CPU、%MEM、VSZ、RSS,人性化带 UID、PID、PPID、C、STIME、TIME典型用途排查 CPU/内存占用、看资源消耗看父子进程关系、看进程树、脚本解析上手难度字段直观,适合人看字段简洁,适合程序读ps aux对人更友好,因为它直接告诉你百分比的 CPU 和内存占用;ps -ef的好处在于有PPID(父进程 ID),适合用来梳理进程之间的血缘关系。我自己平时看资源占用用ps aux,查父子关系用ps -ef,两者各司其职。3. --sort=-%cpu:排序参数里的“隐藏发动机”3.1 排序选项的完整语法--sort=是 GNUps提供的一个长选项,用来指定输出按哪个字段排序。它支持多种排序键,常见的有:%cpu—— 按 CPU 占用率排序%mem—— 按内存占用率排序rss—— 按常驻内存大小排序vsz—— 按虚拟内存大小排序pid—— 按进程号排序time—— 按累计 CPU 时间排序comm—— 按可执行文件名排序关键点在于字段前的符号:加号(可以省略)代表升序,减号代表降序。比如--sort=-%cpu就是“按 CPU 占用率从高到低排”;--sort=rss等价于--sort=+rss,是“按内存占用从小到大排”。你可能好奇,这个排序是在ps内部做的,还是靠外部工具做的?答案是ps内部做了排序再输出。它把所有进程快照抓取一遍,然后根据你指定的键值在内存里排好序,最后按顺序打印出来。所以这条命令天然适合和head搭配,因为head只需要前几行,ps已经把最想看的排到了最前面。3.2 为什么用 %cpu 而不是 time?这是个很容易踩的认知盲区。%CPU和TIME是两回事:%CPU表示进程在一段时间内占用 CPU 的百分比。这个值是ps根据进程的累计 CPU 时间和总运行时间推算出来的,是一个“瞬时/近期”的模糊估计。TIME表示进程自启动以来累计消耗的 CPU 时间(单位通常是分钟:秒)。它是只增不减的“历史账本”。举个例子:一个进程跑了三个月,累计 CPU 时间很长(TIME 很大),但最近几天基本在睡觉(%CPU 几乎为 0)。如果你按time排序,它会排在最前面,但这并不代表它现在是个问题;如果你按%cpu排序,它就会沉到后面去。排查“当前谁在咬 CPU”必须用 %cpu,排查“谁累计消耗 CPU 最多”才用 time。这俩目标不一样,选错排序键就会得出错误结论。3.3 内核态和用户态的隐含信息%CPU反映的是这个进程占用的 CPU 时间片比例,它既包括用户态(执行程序自身的代码)也包括内核态(进程发起的系统调用)。所以当你看到一个进程 %CPU 接近 100%,别急着说“它在疯狂计算”——它也可能是在疯狂做系统调用,比如频繁读写文件、网络请求、内存分配。想让区分更细,可以用ps -o pid,pcpu,time,comm配合top的S模式来看,但日常排查其实不需要分这么细,先把“谁在占用”揪出来再说。实操心得:在单核机器上,所有进程的 %CPU 加起来理论上最高是 100%;在多核机器上,这个上限是 核心数 × 100%。所以如果你看到某个进程 %CPU 是 350%,不要惊讶,它只是吃满了将近4个核而已。我这几年见过不少新手把多核进程当成异常值,其实它可能只是多线程并行任务正常发挥。4. 管道和 head:把“全量名单”裁剪成“前十榜单”4.1 管道符 | 的本质是数据接力在ps aux --sort=-%cpu | head里,|把ps的标准输出(stdout)接到了head的标准输入(stdin)。ps负责把排序后的进程列表一行行吐出来,head负责只读前 N 行,剩下的数据直接丢弃,绝不浪费时间去消费。这有点像自动售货机出饮料:ps是那个把所有饮料一瓶瓶推出来的装置,head是只拿走前面前10瓶的人,后面的饮料没人拿也不会继续倒出来(实际上ps会停止接收head已经关闭的管道,这里就涉及 SIGPIPE 机制了)。具体来说,当head读完前10行就退出时,管道关闭,ps继续写数据就会收到一个 SIGPIPE 信号,默认动作是终止进程。所以这个组合不仅在输出上做了裁剪,在执行上也是“按需生产”,不会傻乎乎地把全部几千行进程列表都格式化完再交付。4.2 head 默认显示几行?head默认输出前 10 行,但注意:ps aux输出里第一行是表头,不是进程数据。所以实际你看到的进程数量是 9 个,不是 10 个。这个问题经常被忽略,很多人以为head给了 10 个进程,其实是 9 个。如果你就是想看前 20 个进程,可以用head -n 20,或者写成head -20。我习惯用-n,因为可读性好,且不会和-20这种数字混淆。4.3 管道组合的常见变体管道的美妙之处在于可以随意插接别的工具,只要你想清楚“上一步输出什么、下一步需要什么”。我的几个常用变体:# 按内存占用排序,取前15名 ps aux --sort=-%mem | head -n 15 # 只看某个用户的进程,并按CPU排序 ps aux --sort=-%cpu | grep username # 动态刷新,每2秒看一次CPU最高进程 watch -n 2 'ps aux --sort=-%cpu | head -n 12' # 彻底去掉表头,纯数据,方便脚本处理 ps aux --sort=-%cpu | awk 'NR>1 {print $2, $3, $11}' | head -n 10watch那个组合我特别推荐,它能把静态的一次性命令变成“会刷新的仪表盘”,虽然不如top全面,但在只想盯住 CPU Top 榜的场景下非常轻量好用。5. 输出字段逐个拆解:看懂每一列的潜台词5.1 从 USER 到 COMMAND 的一张速查表ps aux的默认输出列是固定的:USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND。下面这张表是我压箱底的解释,字段含义结合排查经验一起讲:字段含义实战解读USER启动进程的用户如果是 root,说明该进程有较高权限,排查时需要格外小心PID进程 ID唯一定位进程的编号,kill 指令直接用它%CPUCPU 占用百分比多核下可能超过100,是排序主角%MEM物理内存占用百分比反映该进程占系统总内存的比例,排查内存压力看它VSZ虚拟内存大小(KB)进程“以为”自己占用的内存,含未实际分配的虚拟部分RSS常驻物理内存大小(KB)真正占用的物理内存,看内存应该以 RSS 为准TTY关联终端为?表示无终端(后台服务/守护进程)STAT进程状态D/R/S/T/Z 等,具体解读见下文START启动时间帮助判断是不是“活了很久的老进程”TIME累计 CPU 时间历史累计值,不代表当前活跃度COMMAND完整命令行命令行参数也在里面,方便确认是哪个项目5.2 VSZ 和 RSS:虚拟内存与物理内存的差距我见过不少人在看内存占用时盯着 VSZ 看,以为它是内存占用的真实反映,这是个很常见的误解。VSZ 是虚拟内存大小,它包括了进程映射的所有地址空间——包括那些“申请了但还没碰过的内存页”。举个例子,JVM 启动时往往会预留一整块大内存区域作为堆空间,VSZ 可能显示几十个 GB,但 RSS 可能只有几个 GB,因为大部分页还没被实际写入。真正吃物理内存的是 RSS。排查内存问题,比如 OOM、内存泄漏、内存不足,一定是看 RSS 和 %MEM。VSZ 的唯一价值在于判断程序是否存在“虚拟地址空间耗尽”这类极端问题,日常基本用不上。5.3 STAT 状态码里的字母游戏STAT 列看起来只是一个字母串,但它蕴含的信息量很大。常见的状态码含义:R:正在运行(Running)S:可中断睡眠(Sleeping),等待事件D:不可中断睡眠,通常在等待 I/O,俗称 D 状态T:停止(Stopped),比如被 Ctrl+Z 挂起Z:僵尸进程(Zombie),父进程还没回收子进程残留的 PCBI:空闲内核线程(Idle)另外还会带上一些附加标识,比如<代表高优先级,N代表低优先级(nice),l代表多线程,s代表是会话首进程。比如你看到Rsl,说明这是一个会话首进程、多线程、当前正在运行。实操心得:D 状态是排查故障的重点关注对象。如果大量进程卡在 D 状态,通常是磁盘 I/O 或 NFS 卡死了。这时候ps aux看不出根因,得配合iotop、dmesg查看内核日志才能定位。Z 状态同理,一两个僵尸进程还好,成批出现说明某个父进程出问题了,没有正常调用 wait 系统调用回收子进程。5.4 TIME 列千万不要当成当前 CPU 占用前面已经提过 TIME 是累计值,这里再举一个具体例子。假设你有一个数据库进程,TIME 显示 124:56,这说明它自启动以来累计消耗了约 125 分钟的 CPU 时间。如果它启动了一个月,平均下来每天不到 5 分钟,一点都不高;但如果你昨晚启动了一个任务,才过了 2 小时,TIME 就已经 124 分钟了,那说明它几乎把单核 CPU 全占了。所以 TIME 列要结合 START 列一起看:累计时间除以运行总时长,才是这个进程的平均 CPU 占用率。这层换算关系理解了,排查长期运行的异常进程会快很多。6. 三个实战场景:从“会敲命令”到“会查问题”6.1 场景一:网站变慢,先看谁在抢 CPU假设线上环境突然响应迟缓,登到服务器第一件事就是用ps aux --sort=-%cpu | head -n 15。如果看到某个 Java 应用进程的 %CPU 高达 300%+,而平时它只有 50% 左右,那就说明它内部八成在做大量计算或是在处理一场流量洪峰。下一步我不会直接 kill,而是先top -Hp PID去看这个进程内部的线程(-H 显示线程),找到具体吃 CPU 的线程 ID,再把它转成十六进制去抓线程栈。整个链路追查下来,问题基本都能定位到某个具体的线程和业务逻辑。没有第一行命令的“快速初筛”,后面的所有操作都没有方向。6.2 场景二:内存吃紧,换一个排序键如果机器明明 CPU 不高,但频繁触发 OOM 或者 swap 大量写入,说明内存才是瓶颈。这时候排序键要从%cpu换成%mem,命令变成:ps aux --sort=-%mem | head -n 20你会看到一波“内存大户”排名。很多场景下,排名第一的往往是某个配置了超大缓冲的中间件,或者是有内存泄漏的长期运行进程。观察它的 RSS 是否持续增长,是判断“暴涨但正常”还是“泄漏有问题”的关键。我习惯连续执行几次,间隔 10 到 30 秒,看 RSS 有没有只升不降的趋势。如果稳定涨,基本可以确认是泄漏。6.3 场景三:定位某类进程,结合 grep 精准过滤有时我们不需要全量 Top,只想看某个服务的进程整体占了多少资源。比如想知道所有 Nginx 工作进程的情况:ps aux | grep nginx | grep -v grep注意那个grep -v grep的作用:它把grep nginx这个查询进程本身也出现在结果里的情况排掉了。如果不加它,你会在输出里看到一行grep --color=auto nginx,那只是查询行为本身,不是真实业务进程,会干扰判断。命令里为什么要保留--sort=-%cpu?因为这样可以在同类型进程里快速找到最“突出”的那个,比如某个 Nginx worker 的 CPU 占用明显高于其他 worker,说明这个 worker 正在处理高负载请求,可能是热点流量打到了某一两个 worker 上。7. 常见坑与排查技巧实录7.1 坑一:%CPU 是平均值,不是瞬时值这是ps最容易误导人的地方。ps计算 %CPU 的公式大致是:进程累计 CPU 时间变化量 / 两次采样之间的墙钟时间。也就是说,它是从进程启动(或某个基准点)开始算的“平均占用率”,不是此刻的一瞬间占用率。如果你用ps和top对比同一个进程的 %CPU,往往会发现二者数值不一致——因为top默认显示的是最近 3 秒内的瞬时刷新值。所以,想要“准”的当前占用率,推荐top -b -n 1或者pidstat这类工具。ps的 %CPU 更适合做“趋势判断”而不是“精确测量”。7.2 坑二:head 把表头也算进去了前文提过,head默认取前 10 行,而ps aux的第一行是表头。所以真实拿到的进程数量只有 9 个。这在自动化脚本里尤其危险:如果脚本假设“前 10 行都是进程”,那第一行解析就会出错,得到USER这个字符串而不是 PID。写脚本时要记得awk 'NR>1'或者tail -n +2跳过表头。7.3 坑三:排序键写错导致命令直接报错--sort的排序键是大小写敏感的,比如--sort=-PID会报错,因为正确的键是小写的pid。同时,一些不支持的字段名也会让 ps 返回错误。为了避免这种低级问题,建议敲完命令先跑一次看看有没有报错提示。7.4 坑四:你看到的 COMMAND 可能不完整ps aux的 COMMAND 列理论上会显示完整命令行,但当命令行超长时会被截断。这也会带来排查障碍——当你想区分两个看起来相似但参数不同的进程时,截断的 COMMAND 会让它们看起来一模一样。这时候可以用ps -eo pid,ppid,user,cmd --sort=-%cpu | head,把列换成cmd(完整命令行)或者用-ww参数取消宽度限制:ps auxww --sort=-%cpu | head -n 12ww告诉 ps 不要截断命令行,这样即使参数很长也能完整显示出来。我自己在查 Java 启动参数和 Python 脚本路径时经常靠它救命。7.5 坑五:容器环境下的进程视角如果你在 Docker 容器里执行ps aux,看到的结果是宿主机的全部进程还是容器内进程,取决于容器 init 进程和 ps 工具的行为。现代容器大多共享宿主内核,进程表本质上是一张全系统的进程表,但 PID namespace 会做隔离——容器里只能看到自己命名空间内的进程。如果你发现自己容器里用ps aux看到了一堆宿主机的进程,说明该容器的 PID namespace 没隔离(比如用了--pid=host),或者你直接在宿主机看了。排查容器问题时要先弄清楚这个前提,否则会把别家服务的进程误认成自己的。8. 进阶玩法:按需定制输出格式,彻底摆脱默认范本8.1 用 -o 精确选取自己关心的列ps aux的默认列已经很好用,但有时候我们只想要 PID、CPU 占用和命令,不想看 VSZ、RSS 这些干扰项。-o选项就是为此设计的:ps -eo pid,user,pcpu,pmem,rss,comm,args --sort=-pcpu | head这里-e等价于aux里的“所有进程”,-o后面跟逗号分隔的字段列表。字段名也有讲究:pcpu、pmem是百分比,rss是 KB 数,comm是进程名,args是完整命令行。用-o自定义的好处有两个:一是输出更干净、更聚焦,二是字段顺序完全由你控制,写脚本解析时非常友好。8.2 聚合统计:多个同类进程合并看有时候一个服务会启动很多 worker,你逐个进程看意义不大,更关心的是“这个服务的总 CPU 占用”。这时候ps可以和awk联合作业:ps aux | awk 'NR>1 && $11=="nginx" {cpu+=$3; mem+=$4} END {print "cpu:", cpu, "mem:", mem}'这个命令把所有进程名是 nginx 的 CPU 百分比累加、内存百分比累加,最后输出总和。注意$11字段是 COMMAND 的第一个词,进程名匹配用的是字符串相等,如果你要模糊匹配可以用index($11, "nginx")之类的写法。内存百分比和 CPU 百分比都可以这样聚合,非常实用。8.3 与 top/htop 的配合使用ps aux --sort=-%cpu | head的快照式输出适合“拍一张照片”看现状,top的交互式界面适合“持续观察动态变化”,htop的彩色友好界面和树形展示适合人肉排查。我自己的习惯是:初筛用ps,因为轻量、快捷、可脚本化深入用top或htop,看刷新趋势、按 P/M/T 键切换排序、按 F 添加更多字段长期追踪、后台记录用pidstat或者atop,甚至直接采集/proc数据做监控系统没有哪个工具是万能的,但ps aux --sort=-%cpu | head这一行命令永远是那个“最顺手的第一把刀”。9. 写在最后的几点个人心得命令本身并不复杂,复杂的是你脑子里有没有一套清晰的排查框架。我这些年用下来,最大的体会就是:任何一条命令都值得拆开看。拆懂了ps的 aux 和 sort,你再看top的交互键、htop的样式、pidstat的输出,都会觉得似曾相识——因为它们的底层字段和含义都是同一套体系。还有一个小技巧值得分享:在写自动化脚本时,别直接拿ps aux的 %CPU 字段做精确到小数点后一位的阈值判断。它本身是个估算值,受采样时机影响很大。要做严谨的监控告警,用pidstat或者从/proc/<pid>/stat里自己计算两次采样的差值,才是靠谱的做法。最后想说的是,排查问题的心态比命令熟练度更重要。遇到系统异常,先稳住,按“CPU → 内存 → I/O → 网络 → 内核日志”的顺序一层层查,ps aux --sort=-%cpu | head只是这条链路上的第一步。把它练熟了,后面的每一步都会顺很多。