ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux ps命令完全指南:从进程查看到CPU飙高实战排查

Linux ps命令完全指南:从进程查看到CPU飙高实战排查 你有没有过这样的经历服务器突然卡顿CPU飙升到99%你登录上去想看看发生了什么结果双手悬在键盘上只记得一个命令——ps然后不知道该加什么参数只能敲一个光秃秃的ps回车然后看着屏幕上那寥寥几行进程发呆。我太熟悉这个场景了因为我自己就是从这一步走过来的。ps是Linux下查看进程状态最基础也最常用的命令配合grep、top、kill等工具几乎能解决90%的日常进程排查问题。不过这里得先说明白你在网上搜索“ps”这个词大概率会看到一堆关于Adobe Photoshop下载、破解版安装的教程那是图像处理软件和Linux运维里的ps命令完全两码事。如果你是想学Linux的ps命令那么这篇文章正好适合你——不管是刚接触Linux的新手还是准备面试的运维工程师又或者只是被线上问题逼着不得不查资料的开发同学这篇文章都值得你收藏备用。我会尽量把ps命令的来龙去脉讲清楚。它为什么有那么多看似“不统一”的写法ps aux和ps -ef到底有什么区别STAT这一列里的那些字母都代表什么%CPU和%MEM这两个百分比又是怎么算出来的这些如果你只是机械地记忆参数很快就会忘但如果理解了背后的设计逻辑你就能做到举一反三遇到新问题也能自己推出来该用什么参数组合。这也是我这篇文章想做的事情。1. ps命令的本质与三种风格为什么同一个命令有这么多写法1.1 进程到底是什么搞清楚你要看的东西进程简单理解就是“正在运行中的程序”。程序是静态的它静静地躺在磁盘上比如/bin/bash这个文件一旦你执行它系统就会为它分配内存、CPU时间片、文件描述符等资源这时候它就变成了一个“活”的进程。Linux是一个多用户、多任务的操作系统意味着同一时刻系统里会存在成百上千个进程它们共享物理CPU和内存。ps命令的任务就是把当前系统中这些进程的快照展示给你看。注意“快照”这个词。ps不是实时监控工具它只是在执行的那一刻读取一次系统的进程信息然后打印出来。如果你想持续观察进程的动态变化应该使用top或htop这点我后面会专门讲。但正因为ps足够轻量、输出格式可控它在脚本编写、定时任务、快速排障中反而比交互式的top更好用。1.2 为什么ps会有三种写法UNIX、BSD与GNU风格很多新手被ps搞晕绝不是因为笨而是因为这个命令的历史包袱实在太重了。ps诞生于上世纪70年代的UNIX系统后来BSD和System V两大流派各自发展出了不同的参数风格Linux的ps命令又经过GNU项目的重写整合结果就是你在不同教程里看到的ps命令写法千奇百怪。简单总结就是三种风格UNIX风格参数前带单横线例如ps -ef。这种风格源自System V特点是参数可以组合语义清晰。BSD风格参数前不带横线例如ps aux。这种风格源自BSD系统特点是能输出更多“人性化”的信息比如%CPU、%MEM、STAT这些列就是BSD风格先带起来的。GNU风格参数前带双横线例如ps --sort-%cpu。这是GNU项目对ps的扩展支持长选项可读性最好。问题来了Linux系统里的ps其实是一个融合体它同时支持这三种风格所以你在网上看到的ps -ef、ps aux、ps -aux、ps --pid 1234全都是正确的只是风格不同。但这里有个非常经典的坑ps -aux和ps aux看起来只差一个短横线实际行为可能不一样。在绝大多数Linux发行版上ps aux是BSD风格的写法输出带%CPU、%MEM等列而ps -aux会被解析成UNIX风格系统可能会提示“Warning: bad ps syntax”。虽然现在很多版本做了兼容处理输出结果和ps aux一样但作为规范我还是建议你写ps aux。1.3 为什么推荐你从ps aux入门我自己的习惯是日常查看进程默认就用ps aux。为什么因为它的输出信息最符合直觉。你执行一下看看$ ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 168256 12040 ? Ss 08:12 0:04 /usr/lib/systemd/systemd --switched-root --system --deserialize 17 root 2 0.0 0.0 0 0 ? S 08:12 0:00 [kthreadd] mysql 456 0.1 2.3 1482000 231936 ? Ssl 08:12 0:31 /usr/sbin/mysqld第一行就是表头告诉你每一列的含义USER进程属于哪个用户、PID进程ID、%CPUCPU使用率、%MEM内存使用率、VSZ虚拟内存大小、RSS常驻物理内存大小、TTY进程在哪个终端运行表示非终端启动的守护进程、STAT进程状态、START启动时间、TIME累计占用CPU的时间、COMMAND完整的命令行。说实话这一行表头的信息量就已经很丰富了。VSZ和RSS的区别、STAT里每个字母的含义这些都是面试官爱问的点也是实际排障中的关键我会在后面两章详细拆解。2. 五组最常用的ps场景操作从只会ps到熟练排障2.1 只想快速看全部进程ps aux和ps -ef怎么选如果你只想回答“当前系统有哪些进程在跑”用ps aux或者ps -ef都行。它们都能列出所有进程区别主要在于输出格式ps auxBSD风格多出%CPU、%MEM、STAT等列信息更丰富适合日常排障。ps -efUNIX风格输出的列是UID、PID、PPID、C、STIME、TTY、TIME、CMD其中PPID父进程ID和STIME开始时间更突出适合需要看父子关系的场景。如果你正在写一个需要稳定解析输出的脚本我建议用ps -ef因为它的输出格式更固定不会出现终端宽度问题如果只是人眼去看异常ps aux更直观。这也是我自己的选择逻辑人看用aux脚本用-ef。2.2 精准定位某个进程ps配合grep的正确姿势服务器上有几百个进程你不可能从一堆输出里一条条翻。最常见的做法是把ps的输出管道给grep做过滤$ ps aux | grep mysql root 456 0.1 2.3 1482000 231936 ? Ssl 08:12 0:31 /usr/sbin/mysqld这样就能快速找到和mysql相关的进程。但这里有个新手必踩的坑grep命令本身也是一个进程你在过滤的时候它也会出现在结果里因为它的命令行里包含“mysql”这个关键词。所以你经常看到这样的输出$ ps aux | grep mysql root 456 0.1 2.3 1482000 231936 ? Ssl 08:12 0:31 /usr/sbin/mysqld root 12345 0.0 0.0 112824 988 pts/0 S 10:23 0:00 grep --colorauto mysql最下面那条grep --colorauto mysql就是grep自己。解决这个问题的办法有两种一是用grep -v grep把grep进程本身排除掉比如ps aux | grep mysql | grep -v grep更推荐的做法是直接用pgrep工具它是专门为进程查找设计的$ pgrep -l mysql 456 mysqldpgrep默认按进程名匹配不会匹配到自身用起来干净利落。如果你需要按完整命令行匹配可以加-f参数。这个工具在很多脚本里比ps | grep顺手得多强烈建议你试一试。2.3 进程树与父子关系一眼看出谁是谁的子进程Linux的进程不是孤立存在的除了第一个init/systemd进程其余进程都有父进程。这种父子关系有时候非常关键——比如你发现一个进程占用CPU很高但它的PPID指向了一个异常进程那这很可能就是被“种马”了或者至少说明它的启动方式有问题。想看父子关系可以用ps -ef --forest它能以树状缩进的方式显示进程层级$ ps -ef --forest | head -20 UID PID PPID C STIME TTY TIME CMD root 1 0 0 08:12 ? 00:00:04 /usr/lib/systemd/systemd root 456 1 0 08:12 ? 00:00:31 /usr/sbin/mysqld root 1000 1 0 08:13 ? 00:00:00 /usr/sbin/sshd -D root 1520 1000 0 08:15 ? 00:00:00 \_ sshd: rootpts/0 root 1524 1520 0 08:15 pts/0 00:00:00 \_ -bash是不是清晰直观多了如果你觉得--forest这个长参数不好记还有一个专门的命令pstree输出效果更漂亮默认就有颜色。日常排查父子关系的时候我基本都用pstree -p带PID显示需要嵌入脚本时才用ps -ef --forest。2.4 线程、指定用户、按CPU内存排序高级过滤三板斧有时候你要看的不是进程而是进程内部的线程。Java应用、Nginx worker这类多线程程序的线程状态排查用ps也能做到只是需要参数# 查看PID为456的进程的所有线程 $ ps -T -p 456 # 上面命令等价于下面这个 $ ps -L -p 456输出里LWP那一列就是线程ID轻量级进程SPID是内核线程ID。在排查Java应用CPU飙高问题时先用top -Hp 抓到线程号再结合jstack做线程dump这里的前一步也经常用ps -T -p来替代。按用户过滤、按CPU内存排序也是高频操作# 查看某个用户的进程 $ ps -u mysql # 找出系统里CPU占用最高的5个进程 $ ps aux --sort-%cpu | head -5 # 找出内存占用最高的5个进程 $ ps aux --sort-%mem | head -5--sort后面可以指定排序字段字段前加负号表示降序不加是升序。这个参数非常实用——服务器一卡第一反应就应该是执行ps aux --sort-%cpu | head -10看一眼到底是哪个进程在捣乱。2.5 常用选项速查表不用死记硬背我把平时用得最多的组合整理成了表格建议截图收藏组合命令用途使用场景ps aux查看所有进程的完整信息日常排障首选人人必会ps -ef查看所有进程注重父子关系脚本解析、需要PPID时ps aux | grep xxx按关键词过滤进程快速定位某个程序pgrep -l xxx按进程名查找比pskey更干净ps -ef --forest树状查看进程关系排查父子进程、异常启动ps -T -p PID查看进程的线程列表排查多线程程序ps -u user查看某个用户的进程多用户服务器上抓“罪魁祸首”ps aux --sort-%cpu按CPU占用排序找CPU大户ps aux --sort-%mem按内存占用排序找内存大户ps -eo pid,ppid,stat,cmd自定义显示的列按需输出脚本友好最后一条ps -eo是自定义输出列的写法后面的字段按需拼接-e表示所有进程-o表示自定义输出格式。这个在写采集脚本时特别好用只输出你想要的那些列避免无效信息干扰。3. 输出字段与进程状态解读看得懂才是真会用3.1 STAT状态码全解析进程的“生死簿”ps aux输出里STAT那一列对新手来说最像天书。一堆字母组合比如Ss、Ssl、R、Z这都什么意思我来一张表给你说明白状态码含义说明RRunning/Runnable正在运行或可运行占用CPU或等待调度SSleeping可中断睡眠等待某个事件如IODUninterruptible sleep不可中断睡眠通常在做磁盘IOkill不掉TStopped暂停执行比如收到了SIGSTOP信号tTracing stop被调试器如strace暂停ZZombie僵尸进程子进程已结束但未回收IIdle空闲的内核线程而STAT列里除了主要字母还会出现一些辅助标记例如附加标记含义s会话领导者session leaderl多线程进程multi-threaded前台进程组和终端交互高优先级N低优先级L内存页面被锁定看几个实际例子Ss代表这是一个会话领导者的睡眠进程Ssl则表示它是一个会话领导者、多线程的睡眠进程典型的如mysqldR是前台正在运行的进程和你当前终端直接相关Z这个词一定要记住说明系统里有僵尸进程了。3.2 僵尸进程为什么kill不掉D状态和Z状态的区别这两个状态是面试高频题也是实际运维中最让人头疼的问题。D状态不可中断睡眠的进程通常是在等待磁盘IO或者网络IO完成内核不响应任何信号。你执行kill -9 PID它纹丝不动因为这时候强制终止会让数据不一致内核干脆“装死”拒绝处理。对付D状态进程除了耐心等待IO完成别无他法如果是NFS挂载的超时问题把NFS服务恢复才是正道。Z状态僵尸进程则完全是另一回事。僵尸进程其实已经执行完毕它所占用的资源也已经释放只是内核维护的task_struct结构体还留在进程表里等父进程调用wait()来“收尸”。如果父进程一直不调用wait()僵尸就不会消失。僵尸进程不消耗CPU和内存但会占着PID数量多了会导致系统无法创建新进程。kill -9对僵尸进程没有任何作用因为僵尸已经死了你杀的是一个“尸体”。正确的做法是找到它的父进程重启或杀掉父进程让init/systemd接管并回收僵尸。用上面讲的ps -ef | grep PID就能看到父子关系。3.3 %CPU、%MEM、VSZ、RSS这些数字到底怎么算很多人对%CPU这个数字有误解以为它是指进程目前占用CPU的百分比。实际上ps显示的%CPU是进程累计占用CPU的时间除以进程存活的时间得到的是一个“平均CPU使用率”。所以一个程序刚开始启动时可能瞬间吃满CPU但ps显示的数字反而很低因为它的存活时间太短相反如果一个进程持续占着CPU好几天ps的%CPU会非常稳定地接近100%。注意如果你看到%CPU超过100%别慌那是因为机器有多个CPU核心一个进程使用多核时百分比可以超过100%。比如一个进程用满了两个核心%CPU就会显示接近200%。%MEM的计算相对直观进程的RSS常驻物理内存除以系统的总物理内存再乘以100%。所以内存占用大户一眼就能识别出来。VSZ和RSS这对概念也值得展开说说。VSZVirtual Memory Size是进程虚拟内存大小包括它申请但没有实际使用的内存、共享库占用的内存、内存映射的文件等这个值通常都非常大能到几GB但它不一定代表真实的物理内存消耗。RSSResident Set Size才是进程实际驻留在物理内存中的页大小。简单类比VSZ是你在餐厅点了满满一桌菜包含所有可能用的RSS是你实际吃进肚子里的。如果你发现一个进程的VSZ巨大但RSS很小很可能是程序申请了内存但没有实际使用典型如Java虚拟机启动时预分配堆空间如果两者都很接近且都很大那才是真的内存吃紧。看到这里你可能会想这些参数是不是记住就行。我的建议是结合场景去理解比如解答“为什么我的Java进程内存占用那么高”——打开ps aux看看RSS那列再对比系统总内存你就知道该不该给它加内存配额了。4. 实战排障从现象到技能的完整链路4.1 线上环境排查案例CPU飙高怎么定位你一定遇到过这样的告警某台服务器CPU使用率超过90%。登录上去以后我建议按这个顺序操作第一步确认总体的负载情况执行top按大写P让进程按CPU使用率排序先锁定头部的几个PID 第二步用ps做精确确认$ ps aux --sort-%cpu | head -10这里输出里的第一行除表头外就是罪魁祸首。如果这个进程是应用进程进一步查看它的线程和日志如果是一个无法识别的进程名路径在/tmp目录下那基本可以断定是被入侵了不要犹豫马上隔离服务器导出进程启动命令行和网络连接信息然后kill掉。这里顺便提醒一句备份进程的完整命令行非常关键。ps aux里的COMMAND列默认可能被截断你可以用ps auxww取消宽幅限制把完整命令行打出来有时候排查启动参数问题就必须靠它。4.2 端口被占用、进程杀不掉ps在排查链路里的价值经常有人问“端口被占用了怎么办”确实有专门查端口的命令但如果你在最小化环境里没有netstat也没有ss呢这时候ps也能帮你。先看监听端口的进程# 新版系统优先用ss $ ss -ltnp # 没有ss的旧系统用netstat $ netstat -ltnp这两种命令输出里的PID就是占用端口的进程ID。你拿到PID后回手就是一条ps命令$ ps -fp 1234 UID PID PPID C STIME TTY TIME CMD root 1234 1000 0 09:30 ? 00:00:01 /usr/local/bin/nginx这样你就知道到底是哪个程序在监听端口启动时间是什么父进程是哪一个。再配合ps -ef --forest | grep 1000你还能看到它的启动来源。如果这个进程死活杀掉还自动起来多半是有守护进程在拉它顺着PPID往上一路查下去很快就能揪出元凶。4.3 为什么ps看不到某些进程用户隔离与容器环境有同事问过我我明明用systemctl启动了一个服务ps aux里怎么就看不到这里有两个常见原因。第一个原因是权限不足。ps默认只会显示当前用户能看到的进程如果你想看所有用户的进程必须用ps aux或ps -ef这类“所有进程”的参数如果你连root权限都没有某些进程的信息可能被隐藏看不到很正常。第二个原因是容器环境。现在Docker、K8s环境里你在宿主机上执行ps默认看到的是宿主机上所有的进程当你进入容器内部再执行ps默认只能看到容器自身的进程——因为容器有自己独立的PID命名空间。而且很多精简容器镜像里根本没有ps命令需要先apt update apt install -y procps或者用/proc文件系统自行解析。这点在排查容器内进程状态时特别容易绕弯。4.4 top、htop、ps三件套什么时候用什么ps虽然强大但不是万能的。它输出的是瞬时快照不会自动刷新也不方便持续观察。我的习惯是快速看一次、写脚本、输出到文件归档用ps因为它适合非交互场景执行完就退出稳定可控。持续观察、动态刷新用top按数字1看每个核心的负载按大写H切换线程视图按大写P或M按CPU或内存排序按大写C显示完整命令行。更人性的展示用htop它支持鼠标操作、彩色显示、树状进程查看虽然默认系统不自带但在自己的开发机上装一个排查体验会好很多。很多面试题里会问“ps和top的区别”标准答法就是ps是进程快照执行一次输出静态结果top是实时动态显示它会周期性刷新所有进程的运行状态并且提供丰富的交互快捷键。理解了这层你自然就知道什么样的场景该用哪个工具。5. 新手常见问题与面试考点整理把知识变成自己的5.1 为什么ps aux里的USER列是数字而不是用户名有些场景下你会发现USER列显示的不是root、mysql这种字符串而是一个纯数字。这是因为系统从用户名到UID的映射依赖/etc/passwd文件如果这个用户被删除了或者进程运行在一个与宿主机不同用户表的容器里ps就“翻译”不了用户名只能直接显示UID。你可能会想是不是搞错了什么其实这是正常现象尤其在容器环境里特别常见。知道了这个原理下次看到数字也不会慌。5.2 ps命令在面试中的高频考点整理整理几个我在面试中常问的ps相关题目如果你能用自己的话讲清楚知识点基本就到位了ps aux和ps -ef有什么区别——不同风格aux是BSD风格含%CPU/%MEM/STAT-ef是System V风格侧重PPID/STIME。STAT列的Z代表什么如何处理——僵尸进程等父进程回收通常要处理父进程。%CPU为什么会超过100%——多核并行实际消耗了多个核心。如何用一条命令查看CPU占用最高的10个进程——ps aux --sort-%cpu | head -10。进程RSS和VSZ的区别——RSS是驻留物理内存VSZ是虚拟内存映射大小。线上主机卡顿你用的第一条排查命令是什么——top或者ps aux --sort-%cpu先定位CPU占用大户。这些问题难度不大但答得好不好直接反映平时的实操深度。我建议你不仅记住命令还要理解每个参数背后的设计意图。5.3 一套顺手的分步排查流程分享我根据自己的经验整理了一套通用的进程排查流程照着走基本不会漏# 第1步看整体负载哪个进程在消耗资源 top -bn1 | head -20 # 第2步按CPU/内存排序抓具体对象 ps aux --sort-%cpu | head -10 ps aux --sort-%mem | head -10 # 第3步拿到PID后看完整命令行 ps -fp PID # 第4步看父子关系和启动来源 ps -ef --forest | grep PID 或 父PID # 第5步确认端口等情况 ss -ltnp | grep PID # 第6步如果进程状态异常看是不是僵尸 ps -eo pid,ppid,stat,cmd | awk $3 ~ /Z/这套流程覆盖了“定位、确认、关联、处理”四个环节。你不需要背下来多处理几次线上问题自然会形成肌肉记忆。5.4 最后分享我日常用的几条ps命令组合有些组合我几乎每天都会敲属于刻进DNA级别的操作分享给你# 找出带某个关键词的进程排除grep自身并显示PID和完整命令行 ps aux | grep [j]ava # 把某个服务的所有进程一次性看完包括子进程 ps --ppid 主进程PID -o pid,ppid,stat,cmd # 个人最爱把进程信息导出成可读文件事后慢慢分析 ps aux --sort-%cpu /tmp/cpu_top_$(date %F).txt这里有个小技巧值得说一下ps aux | grep [j]ava这种写法为什么能省掉grep -v grep因为正则表达式里的方括号会匹配字符串“java”中的字符“j”同时grep自身的命令行里包含的是字面上的“[j]ava”不匹配“java”所以grep进程不会出现在结果里。这是一个很老派的技巧但在很多不支持grep -v grep的复杂管道里非常好用。我个人的体会里ps命令最难的从来不是参数本身而是你真正理解“进程”这个概念。当你知道了每个PID背后是一段正在运行的程序、每个STAT字母都代表进程当下所处的状态再去看这些命令就会有豁然开朗的感觉。它不再是一个需要死记硬背的工具而成了你和Linux内核沟通的窗口。希望这篇文章能帮你少走一些我当年走过的弯路。
返回列表