服务器CPU飙高?用top快速定位与持续监控实战指南
服务器CPU报警或者负载飙高的时候大多数人第一个动作就是敲top。我自己也一样不管现在有多少花里胡哨的监控平台遇到性能问题第一反应还是top——它快、轻、哪台机器都有不需要装任何东西。但top这个命令有个尴尬的地方信息密度太高了。第一次接触的人看到满屏数字根本不知道该看哪儿干了好几年运维的人也可能没真正搞懂首部那几行百分比在说什么。这篇文章不准备讲太深的内核原理就聚焦一件事当你面对一台Linux服务器怎样用top快速判断它是不是真的有问题、问题大概率出在哪个进程以及怎样把top从一次性的手工排查变成可以落盘的持续监控。无论你是刚入行的运维、经常要上服务器看日志的后端开发还是准备面试时被问到“怎么排查CPU飙高”的求职者这都算是一份能直接照着用的操作路径。1. 先分清两种用法交互式的“看”和批处理式的“采”很多人用top就只会敲一下回车然后盯着屏幕看刷新。这其实只发挥了它一半的能力。top从设计上就分两种工作模式一种是默认的交互式全屏模式适合人坐在终端前实时观察另一种是批处理模式通过参数直接输出文本结果适合脚本采集和落盘。搞清楚这两种模式是高效使用top的第一步。1.1 交互模式适合人工诊断批处理模式适合脚本采集交互模式就是你直接在终端里执行top屏幕上会持续刷新并且支持各种快捷键操作。这种模式的优点是反馈快适合线上出问题时登录上去手动看一边观察数值变化一边按P按M切换排序快速锁定可疑进程。缺点是输出不稳定每次刷新都会重绘屏幕所以不适合把结果重定向到文件里——你拿到的会是带各种转义控制符的一堆乱码。批处理模式是top -b输出不会清屏重绘而是像普通命令一样一帧一帧打印出来。这个模式通常配合-n指定采样次数使用比如top -b -n 1表示只输出一次当前状态。批处理模式的价值在于它把top变成了一个可以被脚本调用的数据采集工具后面第4部分会专门展开讲。一句话总结人看用交互模式机器采数据用批处理模式别混着用。1.2 上手前先搞懂那几个默认参数top默认每3秒刷新一次这个间隔由-d参数控制。很多人不知道的是这个间隔在批处理模式里同样生效后面做定时采样时会牵扯到执行时长的计算。除此之外有几个参数我几乎天天用-p指定PID只监控某个进程比如top -p 12345。-u按用户名过滤只看某个用户的进程比如top -u nginx。-H切换到线程视图显示进程下的每个线程排查多线程应用卡死时非常关键。-d设置刷新间隔单位是秒也可以传小数比如-d 0.5就是每0.5秒刷一次。还有一个容易被忽略但很重要的参数是-i它会让top隐藏空闲进程。生产环境上几百个进程里大部分都在sleep不加-i的话你根本看不清谁在真正吃CPU所以我个人的习惯是top -i常驻只显示非空闲的进程。1.3 我平时看top的顺序先看负载再看CPU最后定位进程敲下top之后不要一上来就盯着进程列表找谁CPU高而是按“全局→子系统→进程”的顺序来。先看右上角的load average判断系统整体是不是繁忙再看第二行和第三行的进程数与CPU状态百分比判断是CPU计算密集、IO等待还是进程堆积最后才看进程列表用P按CPU排序、M按内存排序具体是哪个进程在作妖。为什么顺序很重要因为同一个现象背后可能是完全不同的原因。比如load average很高但CPU的id也很高说明CPU大量时间在空转这时候问题大概率出在磁盘IO或者不可中断睡眠上你去进程列表里找高CPU进程是徒劳的。我见过不少人一上来就按P看到一个进程500%就急着kill结果load还是没降因为真正的瓶颈根本不在CPU。养成从上到下看的习惯会少走很多弯路。2. 读懂首部数据负载、CPU状态和内存的关联判断top输出的首部是整块信息里最浓缩也最容易误读的部分。很多教程只会告诉你每个字段的字面含义但实际排障的时候这些字段必须放在一起看才有意义。负载高不高要和CPU核数挂钩CPU忙不忙要区分用户态和内核态内存够不够要看缓存和交换分区的联动。2.1 load average三个数字背后的排队逻辑先看load average后面跟着三个数字分别代表过去1分钟、5分钟、15分钟的平均负载。很多人把负载和CPU使用率画等号这是最常见的误解。负载的本质是“运行队列长度”的滑动平均值它统计了两类进程的数量一类是正在CPU上运行的另一类是准备好运行但没抢到CPU时间片的在Linux里还额外包含处于不可中断睡眠状态D状态的进程。因为负载和CPU核数直接相关所以判断负载是否过高的标准是“每个核平均承担多少负载”。4核机器负载到4相当于每个核都被占满8核机器负载到4说明还有一半算力闲着。有个粗略的经验值负载持久超过核数系统就已经过载了超过核数一半就该关注趋势了。另外因为Linux把不可中断睡眠也算进负载所以当大量线程卡在磁盘IO等待上时负载会虚高而CPU使用率反而不高。这个现象在排查数据库或文件服务性能问题时特别常见。2.2 %Cpu(s)那一行才是CPU瓶颈的关键%Cpu(s)这一行包含us、sy、ni、id、wa、hi、si、st几个字段。逐个解释没什么意思我更习惯把它们分成三组来看us用户态sy内核态是真正干活的CPU时间。如果us很高说明大量用户态进程在计算如果sy很高说明系统调用来回切换、内核处理占了太多开销常见于频繁读写、频繁创建线程的场景。id是空闲率但它是扣除wa之后才剩下的空闲所以id高不代表没问题。wa代表CPU等待IO完成的时间占比。这个值一旦持续偏高基本可以断定瓶颈在存储层——磁盘太慢、IO队列太长、或者NFS网络存储抖动。很多新手容易混淆的一点是wa高的时候CPU并没有闲着它只是把时间片都耗在了等IO上。st是steal time主要在虚拟化环境里出现表示CPU时间被宿主机偷走分给其他虚拟机了。你在云服务器上看到st很高说明同物理机上的邻居在跟你抢资源这种情况靠优化自己机器基本无解要么换实例规格要么错峰。2.3 内存行和Swap行怎么结合看内存部分的Mem行显示total、free、used、buff/cache。这里有一个Linux内存管理的特点容易误导人系统会把空闲内存大量用作page cache也就是buff/cache部分这部分内存看起来是“被用了”但实际上只要有进程申请内存内核会随时回收它。所以判断内存够不够不能只看free那一列更合理的口径是free buff/cache减去不可回收部分才是当前可用的近似值。Swap行如果显示used比较高说明物理内存曾经不够用过系统把部分内存页换到了磁盘。swap的使用情况需要结合free里的available字段来看。现代Linux里available是一个比较靠谱的“真实可用内存”估算值它包含可回收的缓存比单纯看free准确得多。如果你发现swap used持续增长同时available不断走低那内存压力确实在累积光清缓存没用得考虑扩容或优化进程内存占用。3. 进程列表的字段与交互操作快速定位元凶首部数据看完了接下来的重头戏是进程列表。怎样在几十上百个进程里快速把问题揪出来靠的是两件事一是认得每个字段的准确含义二是熟练使用交互快捷键。这两件事都不难但缺一不可。3.1 进程行里最值得盯的几个字段进程列表的字段很多但日常排障我基本只看这么几个PID、USER、%CPU、%MEM、RES、S、TIME、COMMAND。RES是进程实际占用的物理内存单位是KB这个字段比VIRT更能反映真实内存消耗。VIRT显示的是进程虚拟地址空间大小它包含共享库、代码段、映射文件等大量并不真正占物理内存的部分所以经常是几GB都不稀奇很多教程喜欢拿VIRT吓唬人实际没意义。判断进程内存泄漏时盯RES就够了。%CPU是进程对单核CPU的占用百分比多核情况下可以超过100%。比如4核机器上一个进程跑满两个核%CPU会显示200%左右。%MEM是进程占用物理内存占总内存的百分比。如果要找内存大户按M按这个字段排序就行。S是进程状态常见的有R运行中、S可中断睡眠、D不可中断睡眠、Z僵尸、T停止。其中D状态说明进程卡在内核态IO等待通常配合wa高一起出现Z状态则说明子进程已退出但父进程没有回收它。这两种状态的排查方向完全不同后面单独说。3.2 常用的排序和过滤快捷键top的交互快捷键是真正提高效率的地方下面这些我基本每次都会用到P按CPU使用率降序排列找谁在吃CPU最快。M按内存占用降序排列排查内存问题首选。T按累计CPU时间排序可以找出长期占用CPU的“慢性子”进程。N按PID排序方便按启动先后找到新起的进程。R反转当前排序方向。u输入用户名只看这个用户的进程。k输入PID和信号值直接给进程发信号默认是15SIGTERM。r重新设置进程的nice值调整优先级。1展开/折叠多核CPU的每个核心状态行看是不是某个核被打满。W把当前配置保存到~/.toprc下次启动自动生效。还有一个容易被忽视的快捷键是c它可以在完整命令行和进程名之间切换。默认显示进程名很多场景下看不出这个进程的具体身份按一下c看完整路径能避免误判。另外按f可以进入字段管理界面用空格键勾选要显示的字段按q保存退出灵活定制适合自己场景的列。3.3 进程状态里的D和Z代表两种完全不同的问题看到D状态进程说明有线程在做不可中断的同步IO操作比如读取磁盘、访问NFS。D状态本身说明不了是“坏进程”它只是告诉我们这个进程在等待IO返回。如果大量进程同时陷入D状态同时wa飙高基本可以判断存储子系统出现瓶颈建议用iostat或iotop进一步确认是哪块盘或哪个挂载点在拖累。看到Z状态进程俗称僵尸进程并不占用CPU也不占用内存所以严格来说它不是资源问题而是程序bug。子进程结束时会发信号给父进程由父进程调用wait()来完成回收如果父进程没做这件事子进程就会变成僵尸。处理僵尸进程的唯一办法是处理掉它的父进程——要么修复父进程的逻辑要么直接重启这个服务的父进程。用户无法强制回收别人家的孩子kill -9对僵尸进程无效这一点很多人试过之后才明白。4. 用批处理模式做持续采样从命令到监控脚本top的批处理模式是我最喜欢的部分因为它让top从“一次性的排障工具”变成“能持续记录现场的数据源”。线上问题最怕的是“复现不了”而定期落盘的top日志相当于给系统的运行状态持续拍照出问题之后翻日志就能看到事发前后的资源变化过程。4.1 -b -n -d的组合逻辑与第一个坑批处理模式的核心参数组合是-b -n -d。-b进入批处理-n指定采样轮数-d指定轮与轮之间的间隔秒数。比如top -b -n 5 -d 2表示每2秒取一次快照连续取5次整个命令大约耗时8秒。输出会按顺序一帧一帧打印进程列表在每帧重复出现可以直接重定向到文件。这里有一个我踩过好几次的坑top -b -n 1的第一次采样数值经常“不准”。原因在于top计算CPU使用率时用的是两个采样点之间的差值而第一次采样没有前值它会用系统启动时间作为起点来计算平均值所以你看到的%CPU和%Cpu(s)可能是“自开机以来的平均”而不是“当前的值”。这正是很多人在脚本里发现CPU明明已经飙高但top -b -n 1显示正常的原因。解决方式很简单取两次采样丢掉第一次用第二次之后的结果也就是top -b -n 2 -d 1然后从输出里取后半部分。4.2 一个能直接拿来用的采样脚本下面这个脚本是我在服务器上实际用过的简化版它会每隔5秒采集一次系统的首部信息和Top进程追加写入日志文件。脚本里保留了“取第二次采样”的经验也通过临时文件避免了管道截断对top采样的影响#!/bin/bash # /usr/local/bin/topmon.sh LOG_FILE/var/log/topmon.log INTERVAL5 while true do # 取两次采样第二次的CPU百分比更有参考价值 top -b -n 2 -d 1 /tmp/topmon.$$ 2/dev/null { echo echo $(date %Y-%m-%d %H:%M:%S) head -n 35 /tmp/topmon.$$ } ${LOG_FILE} rm -f /tmp/topmon.$$ sleep $((INTERVAL - 2)) # top采样本身耗时约2秒补偿一下 done跑起来之后tail -f /var/log/topmon.log就能实时看到系统状态。每次记录前50行左右既包含首部全部信息也包含CPU占用最高的Top进程。日志会随时间增长建议配合logrotate做轮转按天或按大小切割免得日志把磁盘塞满。4.3 采样间隔、落盘节奏与日志清理采样间隔不是越小越好要结合业务场景和磁盘空间来定。一般故障现场排查只需要秒级采样但如果要长期稳定运行建议间隔至少10秒以上。日志的增长量很容易估算一次快照约3到5KB5秒一次就是每小时约3MB一天约70MB。如果你有10台机器都这样采一个月就是20多GB。所以我通常建议间隔拉长到30秒到60秒只保留7到14天日志磁盘紧张就再缩短。还可以考虑按进程维度过滤减少无用数据。比如top -b -n 2 -d 1 -p 12345只监控指定PID或者配合-u只采集某个用户的进程这样日志体积能缩小很多。另外如果只关心CPU和内存的顶部状态可以不用head截取而是配合grep加awk从输出里提取关键行这样写入的只是几行汇总数字长期监控的成本低得多。5. top数据从哪来以及几个容易误判的坑最后这部分说说top的运行机制和几个我认为最容易踩坑的地方。理解数据来源能帮你解释很多“诡异”现象而知道它的边界能帮你在合适的场景下选用更合适的工具。5.1 top背后的/proc数据源top本身不是一个性能采集工具它更像一个“读卡器”所有数据几乎都来自Linux的/proc虚拟文件系统。首行的load average读取的是/proc/loadavgCPU各状态百分比来自/proc/stat内存信息来自/proc/meminfo每个进程的CPU和内存数据则分布在/proc/[pid]/stat和/proc/[pid]/status里。这意味着两件事第一top不会额外增加太多系统开销第二如果你在容器里运行top看到的可能是宿主机的全局数据而非容器自身的资源限制视图因为/proc是内核暴露的容器技术并没有完全隔离它。这在使用Docker/K8s排查问题时是一个很容易被忽略的坑。理解了数据源还有一个实际价值当你怀疑top显示不更新或者数值异常时可以手动去看/proc/stat里的cpu行。这一行从系统启动开始累计递增top只是每隔一段时间读一次算差值。如果你在脚本里想自己计算CPU使用率直接读这个文件是最底层的做法很多监控agent就是用它来算的。5.2 多核百分比超过100%不是bug我收到过好几次类似的疑问“为什么有个进程CPU占用显示300%是不是算错了”这其实不是bug它恰好说明进程利用了多核并行。前面提到过top里的%CPU是相对单个CPU核心来计算的所以在4核机器上一个进程最多能显示接近400%在8核机器上能显示接近800%。看到超过100%的数值不要大惊小怪结合机器的核数来判断这个进程是不是真的吃满了资源。真正容易被误判的是%Cpu(s)这一行顶部的合计值。不管机器有多少核这一行的us、sy、id等都是按照所有核心加总后再归一化的百分比所以它们的合计约等于100%乘以核数但实际上top显示的是归一化后的总和基本在100%左右。这跟进程的%CPU表示方式不是同一个口径放在一起看的时候很容易把自己绕晕。记住进程的%CPU超过100是正常的首行的CPU状态百分比也根本不需要凑成100%。5.3 什么场景下top不够用需要搭配其他工具top擅长回答“现在系统整体负载高不高、哪个进程最活跃”但它的粒度不够精细它看不到是哪个磁盘分区在忙看不到具体是哪个网络连接在消耗带宽也看不到系统调用层面的耗时分布。所以真正深挖问题时我一般会用它先做初筛再针对性上更精确的工具。如果wa高、大量进程处于D状态用iostat -x 1看每块盘的%util和await能直接定位到坏盘或者慢盘如果si高、网络包处理消耗大量CPU用perf top或者netstat -s看协议栈的统计如果经常出现sy偏高用pidstat -w看看进程的上下文切换次数可能是线程数太多引起的锁竞争。对于监控告警top的批处理模式最多算是轻量方案生产环境要搭正经监控还是得用node_exporter加Prometheus这类体系top适合的是快速定位和不方便装agent的场合。我在实际使用中还有一个小体会top看久了会产生一种“数值焦虑”特别是看到负载忽高忽低、CPU百分比跳来跳去的时候。但性能排查讲究的是持续性单次快照说明不了问题至少观察10秒到1分钟的走势综合多个采样点再下判断。top给你的是一张地图而不是一条结论真正有价值的还是你基于这些数据形成的判断链路。