Linux top命令详解:从load average到OOM的性能排查 盯着机器开始变卡的时候我第一个敲出去的往往是 top。它几乎存在于每一台 Linux 机器上不用装、不用配 agent、不依赖任何外部服务一个回车就把此刻谁在吃 CPU、谁在占内存铺在你眼前。但 top 也是我见过被误读最狠的命令之一有人看到 load average 是 8 就断定机器要炸结果是台 16 核机器有人看到 %CPU 显示 250 就以为程序有 bug更多人是看到 Mem used 明明没满 却发生 OOM一脸茫然。top 命令的内容说明表面上是背几个字段实际上是要搞清楚每个数字的定义、来源和它的不适用场景。这篇东西面向的是所有需要登机器看现场的人刚入门运维的、写服务端程序需要自己定位性能的、以及那些在被监控大盘喂了二手数据之后想回到一手现场的人。我会把 top 界面的每一行、每一列拆开讲清楚说明它统计的是什么、为什么这么统计、什么情况下会骗你再给几套可以直接抄的操作方式和一份常见问题速查表。读完之后你至少应该能做到看到一屏 top 输出三分钟内说清这台机器现在是被 CPU、内存、IO 还是负载结构拖住了。1. 先搞清楚 top 界面的设计逻辑1.1 top 解决的问题和它的第一选择场景top 的定位一句话概括以固定间隔重复采样实时展示系统整体资源汇总和按资源排序的进程列表。注意重复采样这四个字它决定了 top 和 ps 的根本差异。ps 是一次性的快照你敲完的那一刻进程表就定死了top 是持续刷新的观察窗口你能看到某个进程的 %CPU 从 3% 跳到 97% 再掉下来这种变化过程才是定位问题的关键。所以排查正在发生的问题时top 是首选排查已经发生过又消失了的问题时top 无能为力得靠 pidstat、atop 这类带历史记录的工具。它还有一个常被忽略的优点数据全部来自 /proc 这个伪文件系统采样成本极低不落盘、不占网络、不需要额外权限普通用户能看自己视角下的信息root 能看全量。这意味着你在一台已经卡到 SSH 都难登的机器上top 往往还是能跑起来的因为它的开销基本可以忽略。注意top 默认的刷新间隔是 3 秒。对突发型负载来说3 秒的粒度经常会漏掉尖峰尤其是那些存活时间只有几百毫秒的短任务。看到机器卡但 top 上一切正常第一反应应该是把间隔改小而不是判断机器没问题。1.2 界面的三层结构汇总行、任务行、进程表一屏 top 输出其实分成三块很多人的困惑来自把三块的数字混着读。第一块是最上面一到两行的头部信息当前时间、机器已运行时长、登录用户数、load average 三个值。第二块是汇总区也就是 Tasks、%Cpu(s)、KiB Mem、KiB Swap 这几行描述的是整个系统的状态统计口径来自 /proc/stat 和 /proc/meminfo。第三块是进程表每一行对应一个进程或者开了线程模式后对应一个线程列出自上而下的各个字段。这三块的采样时刻、统计口径、甚至数值来源都不一样。比如汇总区的 %Cpu(s) 是所有 CPU 核心合并计算的平均值而进程表里的 %CPU 是单个任务的占比汇总区的 Mem used 有明确的减法定义而进程表里 RES 列的加总跟它没有严格的加总关系。搞混这一点就会出现所有进程 %CPU 加起来不等于 100%这种没必要的困惑——在多核机器上本来就不该等于 100%。1.3 为什么排障时先看 top 再上监控大盘监控系统无论是自建还是托管给的是趋势和告警延迟通常在 15 秒到 1 分钟之间而且指标往往已经过了一轮聚合。它擅长回答这台机器过去一小时是不是持续偏高不擅长回答现在这一秒到底是什么在跑。我自己的习惯是先 top 看现场确定是哪一类资源被吃满、是哪个进程干的然后再去大盘上回看这台机器在出事前后的趋势判断这是长期趋势还是突发。顺序反过来会有一个坑你在大盘上看到一个 CPU 使用率 85% 的曲线回去看 top 一切正常最后发现那 85% 是平均到整个机柜口径的、或者是被某个周期性任务拉起来的、或者已经过去了。先现场后历史能省掉大量来回确认的时间。2. 汇总区五行逐字拆解最容易被误读的数字2.1 load average 的三个数字到底代表什么第一行大概长这样top - 14:32:07 up 12 days, 3:41, 2 users, load average: 4.12, 2.87, 1.95。三个数字分别是过去 1 分钟、5 分钟、15 分钟的平均负载。这里最容易出错的不是数字本身而是对负载这个概念的理解load average 统计的是在采样周期内处于可运行状态R和不可中断睡眠状态D的进程数量平均值不是 CPU 使用率。因为包含了 D 状态所以在大量磁盘 IO 把进程卡在不可中断睡眠时负载会飙高而 CPU 可能很闲。这就是load 高 CPU 不忙的经典解释。反过来说一台满负荷跑计算的机器每个核上有一个线程在排队load 就会稳定在核数附近CPU 使用率接近 100%这是健康的高负载。判断标准别记绝对值记比值用 load average 的 1 分钟值除以逻辑核数nproc的结果。比值小于 0.7 基本可以不管0.7 到 1.0 之间要注意趋势超过 1.0 说明有任务在排队等待超过 2.0 就属于明显的资源争抢了。至于看哪个时间窗口靠三个数的关系判断方向——1 分钟值远大于 15 分钟值说明问题刚起来反过来说明在缓解。实操心得load average 是全局的不受容器隔离影响。在一台宿主机上跑了多个容器时你在容器里执行 top 看到的 load 是宿主机整体的不是你这个容器贡献的。看到容器里 load 很高但进程表里没几个进程先别怀疑自己去看看宿主机上还有谁在跑。2.2 Tasks 行与五种进程状态的判别价值Tasks 行形如Tasks: 312 total, 2 running, 308 sleeping, 0 stopped, 2 zombie。这一行信息密度不高但running和zombie两个数字值得多看一眼。running 数量持续大于逻辑核数说明确实有排队和 load 的结论互相印证。zombie 数量如果持续增长说明父进程没有正确回收子进程属于代码层面的问题跟机器资源没关系处理方式是找父进程而不是重启。真正有价值的是进程表里的 S 列它把状态标得更细R运行中或待运行S可中断睡眠绝大多数空闲进程都是这个D不可中断睡眠通常卡在 IO 上这个状态最需要警惕T被信号暂停Z僵尸。还可以看到I表示内核空闲线程。D状态为什么关键因为处于这个状态的进程既不响应信号连 kill -9 都要等它退出系统调用也不占用 CPU。你如果发现一批进程长时间卡在 D基本可以确定是存储层的问题——磁盘打满、网络存储挂起、文件系统异常。此时 top 的 wa 字段通常也会同步升高两个信号互相佐证。2.3 %Cpu(s) 八个字段从 us 到 st 逐个说这一行是%Cpu(s): 12.3 us, 4.1 sy, 0.0 ni, 78.5 id, 4.8 wa, 0.2 hi, 0.1 si, 0.0 st。八个字段的可读解释如下字段含义观察重点us用户态 CPU 时间占比业务程序占用通常主体应该是它sy内核态 CPU 时间占比持续偏高说明系统调用、上下文切换多ni调整过 nice 值的进程占用一般很小出现说明有人手动调过优先级id空闲和 wa 的关系见下文wa等待 IO 完成的时间超过 10% 要关注存储hi硬中断处理网卡大量收包时会升高si软中断处理网络转发型服务常见偏高st被虚拟化层抢占的时间云主机上才有意义几个容易踩的坑。第一sy偏高不一定是内核有问题很多系统调用密集的程序频繁 fork、大量小文件读写、频繁创建销毁线程本来就会把 sy 拉高判断依据是 sy 和 us 的比例是否合理而不是 sy 的绝对值。第二wa和id的关系有个反直觉的点从内核统计口径看iowait 时间 CPU 确实是空闲的所以它也被计入 idle 分母top 是把它从 idle 里单独拎出来显示。这意味着id 高 wa 高是可以同时出现的不矛盾。真正要警惕的是wa持续超过 20%那基本能确定瓶颈在存储侧。第三st是云环境里最该懂的一个字段。它表示虚拟机想跑但被宿主机调度出去的时间。st 持续超过 5%说明你这台实例的 CPU 配额被邻居挤占了这时候你机器里做什么优化都没用该做的是调整实例规格或者排查是否触发了 CPU 积分限制。2.4 Mem 与 Swap 行available 和 free 从来不是一件事较新版本的 top 中这两行长这样KiB Mem : 8123456 total, 1234567 free, 3456789 used, 3432100 buff/cache下一行是KiB Swap: 2097148 total, 2097148 free, 0 used. 4012345 avail Mem。第一个要建立的概念是free 小是正常的不是问题。Linux 会把空闲内存拿去做页缓存buff/cache这是提升性能的设计不是内存泄漏。真正该看的是行尾的avail Memavailable它表示在不触发交换的前提下还能分配给新应用的内存估算值包含了大部分可回收的缓存。判断内存是否紧张正确姿势是看 available 占总内存的比例。available 低于 10% 就要开始关注低于 5% 随时可能触发回收甚至 OOM。另一个信号是 Swap 行的 used 和进程表里进程的 swap 占用如果 used 持续增长且 si/so 非零说明确实在换入换出性能会明显下降。还有一个版本差异要注意老版本 top 的 Mem 行没有 available 这项只有 free 和 buff/cache那时候需要自己心算free buff/cache做粗略估计或者去读 /proc/meminfo 里的 MemAvailable。如果你在跳跃不同发行版机器别因为字段少了一列就以为命令坏了。3. 进程区字段从一屏里把元凶揪出来3.1 PR 与 NI优先级的两个数字别搞反进程表里的 PR 列是优先级NI 是 nice 值。它们的关系是PR 20 NI对普通进程而言。所以 nice 为 0 的进程 PR 显示 20nice 为 -5 的进程 PR 显示 15。实时进程调度策略为 SCHED_FIFO 或 SCHED_RR的 PR 显示rt这类进程优先级高于所有普通进程出现时要留意是否有服务把自己设成了实时调度。NI 的取值范围是 -20 到 19值越小优先级越高只有 root 能把 nice 调成负值也就是提升优先级普通用户只能在 0 到 19 之间往上调降低自己的优先级。这个设计是为了防止普通用户抢占系统资源。实际操作中如果发现某个批处理任务把 CPU 吃满了但又不紧急可以用 top 里的r键交互式调整它的 nice 值把它压到 19让业务进程优先。这比直接 kill 温和得多也是我常用的一个应急手段——先把不紧急的任务降权等高峰期过去再说。3.2 VIRT、RES、SHR三个内存指标的区别和误判这是 top 里误解最多的一组列。VIRT是虚拟内存总量包含进程申请的所有地址空间已经映射的共享库、已申请但还没真正写入的匿名内存、甚至 mmap 映射的大文件。它几乎总是比实际占用大得多看到 VIRT 显示几十 G 完全不奇怪尤其是有内存池、JVM、Go runtime 这类预申请机制的程序。VIRT 大不代表内存被吃了这是最重要的一句话。RES是常驻内存指当前真正在物理内存里的部分不含被换出的。这个数字更接近这个进程实际占了多少内存但要注意它包含了共享部分比如多个进程共用的 libc 库会被各自计算一次。SHR就是那部分共享内存。有些人喜欢用RES - SHR当作进程独占内存这个算法不严谨——SHR 里既有真正多进程共享的部分也有私有文件映射减出来的结果只能当粗略参考。更准的做法是看 /proc/PID/smaps_rollup 里的 Pss按共享比例分摊后的值或者用 smem 工具。还有一个直觉陷阱把进程表里所有 RES 加起来结果远大于 total 内存这很正常因为共享库被重复计算了。别拿这个去证明内存统计有问题。3.3 %CPU、%MEM、TIME 的判读细节%CPU表示该进程在上一个采样间隔内占用的 CPU 百分比。默认采样间隔 3 秒所以你看到的是一个 3 秒的均值不是瞬时值。单个单线程进程的 %CPU 上限是 100%而多线程进程可以超过 100%——比如 4 线程满载会显示 400%理论上限是 100% × 线程数。看到 250% 一点不奇怪那是 2.5 个核在跑。真正要看的是它和总核数的关系在 8 核机器上某个进程 300%意味着它吃掉了 37.5% 的总算力。%MEM是 RES 占总内存的百分比。它比 %CPU 更稳定适合用来找内存大户但同样受共享内存重复计算的影响。TIME是进程累计消耗的 CPU 时间格式是分:秒.百分秒比如123:45.67。这个字段的价值在于判断长跑型和突发型一个进程 %CPU 很高但 TIME 很小说明它是刚起来的%CPU 不高但 TIME 巨大说明它已经积累了很长的 CPU 时间往往是常驻服务。在排查机器最近变慢时TIME 能帮你区分是老进程一直在耗还是新进程突然爆发。另外进程表最后还有一列COMMAND。默认只显示命令名按c键会切换成完整命令行。这个键非常重要——很多问题程序的名字看起来人畜无害比如都叫 java、python只有看完整命令行才知道是哪份配置、哪个参数启动的。4. 动手把 top 用成一份可复现的排障记录4.1 高频交互键与它们背后的意图top 运行起来之后可以敲一系列单键来改变显示。常用的那些P按 %CPU 排序默认就是这个但如果你切走过别的排序这个键能切回来M按 %MEM 排序找内存大户时第一时间敲它T按 TIME 排序找累计 CPU 消耗最多的进程1展开显示每个逻辑核的独立使用率这是判断负载均衡的关键——总使用率 50% 有可能是所有核一半也有可能是两个核满载其余空闲后者说明程序并发没做对c显示完整命令行H切换线程模式把每个线程单独列出来排查多线程程序的单线程热点必用u按用户名过滤多用户共用机器时很有用i只显示正在运行的进程把大量睡眠进程折叠掉屏幕瞬间清爽k发信号给进程本质是 killr调整 nice 值z打开彩色显示x高亮当前排序列y高亮正在运行的行W把当前设置写成配置文件保存下来那个1键值得单独说。我见过太多次CPU 使用率只有 50% 但系统非常慢的案例展开之后发现是 4 核里 2 个跑满、2 个几乎空着。原因是程序的并发模型有问题要么只有单线程在干活要么多个线程在抢同一把锁。这种情况下加机器核数没用得改代码。如果不敲这个键你只会看到一个温和的 50%完全找不到问题。4.2 批处理抓快照采样、留证、写进工单交互式 top 的数据看完就没了事后复盘或者要贴给同事时很不方便。用批处理模式可以把输出落到文件里# 采 60 次每秒一次输出到文件 top -b -d 1 -n 60 top_snapshot.txt # 只看汇总和进程表头不要一屏屏的进程明细 top -b -n 1 -o %CPU | head -20 # 针对特定进程持续采样间隔 2 秒采 30 次 top -b -d 2 -n 30 -p 12345 pid_12345.txt-b是批处理模式batch输出不带终端控制字符可以直接重定向-d是间隔秒数支持小数如-d 0.5-n是采样次数采完自动退出。-o用于指定排序字段较新的 procps-ng 支持比如-o %MEM、-o RES。有几个细节要留意。第一-n 1采出来的第一屏数据里%CPU 是从系统启动到现在的累计均值不是瞬时值——因为 top 需要两次采样才能算出增量第一次没有参照。所以只采一次的时候%CPU 那一列基本没用要看它至少采两次以上用第二次之后的结果。实操心得出问题时抓现场我的做法是同时存两份一份top -b -d 1 -n 30的原始输出用于事后复盘一份top -b -n 2 -d 1 -H -p PID的线程级快照用于确认热点线程。两份加起来覆盖一分钟的现场比事后凭记忆描述强得多。文件抓取时最好把date的输出一起写进去方便对齐时间轴。4.3 只看某个进程、某个用户、看线程当已经从汇总区判断出问题方向就可以收窄观察范围了。# 只看指定 PID可以跟多个 PID top -p 12345,12346 # 只看某个用户的进程 top -u appuser # 线程视图配合 -p 定位某个进程内部的线程 top -H -p 12345线程模式-H在排查 Java、Go、C 这类多线程服务时特别有用。你能看到同一个 PID 下面挂着若干个 TID哪个线程把 CPU 吃满了。更进一步的做法是把线程 ID 转成十六进制printf %x\n TID然后去 jstack 或者 perf 的输出里搜就能对应到具体是哪段代码在跑。这个链路我用了很多年从 top 到代码路径的定位效率非常高。-u过滤在多租户机器上很实用尤其是那种一台机器上跑了好几个业务方进程的情况先按用户圈定范围再看能避免被无关进程干扰视线的。4.4 保存布局与配色让每次打开都一样改了一堆显示设置退出就没了下次还得重来。按W键top 会把当前配置写入家目录下的.toprc较新版本改成了.config/procps/toprc下次启动自动加载。这里面保存了排序列、刷新间隔、是否显示命令行、颜色方案等。如果你的习惯是每次都要c和1都打开直接按 W 存下来最省事。团队里如果多个人都要登同一批机器可以把一份标准 .toprc 放到统一位置避免每个人看到的界面不一样导致沟通错位——这个细节在协同排障时比想象中重要讨论问题时大家的列顺序一致沟通成本能低不少。另外s键和d键可以在运行时直接改刷新间隔。默认 3 秒偏慢我一般调到 1 秒看实时变化真要抓尖峰再调到 0.5 秒。不过间隔太短会让 top 自身占用更多 CPU且数字抖动得厉害反而看不清趋势1 秒是个比较舒服的平衡点。5. 常见问题与排查速查表5.1 %CPU 超过 100% 是不是程序有 bug不是。这是多核系统下最常见的误解。%CPU 的分母是单个核心不是整机。所以一个 8 线程全速运行的程序会显示接近 800%。要看它占整机的比例再除以逻辑核数。真正需要警惕的是另一种情况单线程程序长期跑在 100%且业务吞吐上不去。这说明这个线程是瓶颈需要做并发改造如果它本来就是单线程设计的守护进程那 100% 就是满载了该扩容或者优化算法。用-H展开线程视图能很快判断到底是一个线程跑满还是多个线程平分。还有一种情况是 %CPU 加起来远超核数总和这通常出现在 IO 密集场景下多个进程同时在 D 状态和 R 状态之间切换统计上会呈现叠加效果不必惊慌去看 wa 和 sy 更实际。5.2 load average 很高但 CPU 空闲这是 D 状态进程堆积的典型表现也是 top 使用中最值得掌握的一个判断。执行路径是这样的先看%Cpu(s)里的wa如果 wa 也高那基本确定是存储侧如果 wa 不高但 load 高那就得直接找 D 状态进程。# 从 top 里按 S 列观察或者直接过滤 ps -eo pid,stat,wchan:20,comm | awk $2 ~ /D/wchan列会告诉你进程卡在哪个内核函数上比如io_schedule、nfs_wait、blkdev_issue_flush之类。看到这些就知道是哪个存储层在拖。常见的触发原因包括磁盘被打满导致写操作排队、网络存储服务端响应变慢、文件系统元数据锁竞争、大量小文件同步写。还有一种容易被忽略的情况load 高 CPU 也高但进程表里看不到吃 CPU 的进程。这时看一眼1键展开的单核视图如果所有核都比较均匀地高那可能是大量短命进程不断创建又退出——top 每 3 秒刷一次根本抓不住它们。这种场景得换 pidstat 或者用top -d 0.5把间隔压下来看。5.3 内存没满却发生 OOMA 和 B 两个数字经常被混淆Mem used是 top 按total - free - buff/cache算出来的展示值而内核决定是否 OOM 的依据要复杂得多涉及各个内存 cgroup 的限制、页缓存的可回收性、slab 占用等等。几个常见的误判原因。一是容器限制进程跑在 cgroup 里cgroup 的内存上限可能远小于宿主机总内存top 显示的是宿主机的总量看起来还剩一大半实际上容器配额已经到顶了。排查要看 cgroup 的 memory.max 和 memory.current。二是内存碎片总量还有空间但找不到连续的大块申请大页失败。三是overcommit 策略系统允许超额申请真正写入的时候才发现不够。注意top 显示的 Mem 数据来自 /proc/meminfo在容器环境里默认是宿主机的数据除非做了隔离处理。容器里排查内存问题不能只信 top要同时看 cgroup 的统计文件否则结论会完全跑偏。5.4 wa、si、so、st 非零分别意味着什么这几个字段单独拎出来说因为它们的判断价值很高但经常被忽略。wa非零并持续走高指向存储。注意是持续偶发的 wa 波动很正常比如定时任务落盘、日志轮转。持续超过 10% 就要去查磁盘队列深度和 IOPS 是否打满。si和so是 Swap 的换入换出速率单位是 KB/s。这两个值只要持续非零就说明物理内存不够用了系统在拿磁盘当内存用性能会呈断崖式下降。注意它们不在 %Cpu(s) 那一行而在 Swap 那一行的末尾。看到 si/so 持续偏高第一件事是找内存大户并按 M 排序而不是去调内核参数。st是 steal time只有在虚拟化环境下才有意义。它表示物理 CPU 分配给别的虚拟机、导致当前虚拟机想跑却没跑成的时间。st 超过 5% 说明你的实例在跟邻居抢 CPU这种问题在实例内部怎么优化都没用。几个云厂商的做法不同有的靠 CPU 积分制积分耗尽就会限制性能表现出来就是 st 升高加 CPU 使用率上不去这个现象非常迷惑人。5.5 一份可以直接抄的速查表现象大概率原因下一步动作load 高wa 高D 状态进程多存储侧瓶颈查磁盘 IOPS、队列深度、网络存储延迟load 高CPU us 高真实计算压力按 P 排序找大户看是否可扩容load 高CPU 空闲wa 不高可能是短命进程或不可中断等待缩小采样间隔查 wchansy 占比异常高系统调用密集、上下文切换多查线程数、看 vmstat 的 cs 列si/so 持续非零物理内存不足按 M 排序查是否有泄漏或缓存失控st 持续超过 5%虚拟化层资源争抢检查实例规格、CPU 积分、宿主机状态%CPU 超 100%多核多线程正常现象除以核数看整机占比无需处理某进程 %CPU 100% 且吞吐不涨单线程瓶颈用 -H 看线程考虑并发改造RES 之和远大于总内存共享内存重复计算正常别当 bug需要精确值看 Pssavailable 低于 5%内存将耗尽立即找大户准备扩容或限流进程长期 Z 状态父进程未回收子进程查找并修复父进程逻辑各核负载严重不均程序并发模型有问题按 1 展开单核视图确认改代码而非加核关于这台机器的现场判断我个人习惯的顺序是先扫一眼 load 和可用核数的比值定基调再看 %Cpu(s) 里 wa 和 st 有没有异常然后按 M 确认内存余量最后才按 P 钻进进程表找具体是谁。这个顺序的好处是先排除系统层面的硬约束避免在一个明显是被存储拖垮的机器上死磕某个进程的 CPU 占用。走熟了这个流程之后大部分现场问题看两眼就能定性剩下的时间都花在确认和验证上。