Linux top命令详解:从输出含义到CPU、内存与I/O排障实战
1. 为什么top调用的输出总让人“看不懂”——先搞清楚每个区块在讲什么top命令大概是每个运维人除了ls之外敲得最多的一条命令。服务器一卡下意识就是登上去敲个top看一眼。但说句实话我见过太多人包括几年前的我看着屏幕上一群数字内心其实是懵的——load average是啥意思us和wa哪个高才算有问题VIRT和RES为什么差着好几个数量级%CPU明明显示400%这又是哪来的如果你也只会按q退出那今天这篇可以直接收藏了。我会把top输出的每一行、每一个字段都拆开揉碎讲清楚它背后的含义再结合我实际排障的经验告诉你什么情况下该盯哪个数字、什么情况下数字正常但你系统其实已经出问题了。先说一个结论top的输出虽然是“实时刷新”的但它实际上是一组采样的快照。你看到的所有百分比、所有状态统计都是在过去的某个时间窗口内计算出来的平均值或瞬时值。很多误判恰恰是因为没搞懂“采样”这两个字。后文我会专门展开讲这个坑。先贴一段标准的top输出CentOS 7 / Ubuntu 20.04默认配置下基本一致top - 14:23:05 up 68 days, 3:42, 2 users, load average: 0.08, 0.02, 0.01 Tasks: 123 total, 1 running, 122 sleeping, 0 stopped, 0 zombie %Cpu(s): 2.1 us, 0.7 sy, 0.0 ni, 97.1 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 3882420 total, 593920 free, 1428424 used, 1860076 buff/cache KiB Swap: 0 total, 0 free, 0 used. 2029244 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 1208 root 20 0 279216 38416 11884 S 2.0 1.0 62:16.61 dockerd 1540 root 20 0 166636 3644 2868 R 1.7 0.1 0:00.04 top你盯着这块屏幕想知道两件事第一这台机器现在到底紧不紧张第二如果紧张是哪个进程在搞鬼。接下来的章节就是围绕这两件事展开的。2. 系统概览区load average、CPU状态和内存行的真实含义2.1 第一行的uptime信息与load average解读第一行从左到右依次是当前时间、系统已运行时长、登录用户数、load average。up 68 days, 3:42代表这台机器已经连续开机68天。这个数字本身没太多意义但如果你的机器uptime很短说明它刚重启过——这往往是一条值得追问的线索是谁重启的是不是之前内存溢出被运维重启了还是被OOM Killer反复折磨的结果我排查问题的时候习惯先看一眼这个值因为“开机三分钟就load飙升”和“运行半年后load飙升”完全是两个排障方向。load average后面的三个数字是整台机器“忙碌程度”的综合指标。三个值分别是过去1分钟、5分钟、15分钟的平均负载。这里必须破除一个最大的误解load average不是CPU使用率的百分比它统计的是处于可运行状态和不可中断睡眠状态的进程/线程的平均数量。换句话说它是一个“排队数”而非“占用率”。如果把CPU比作收银台load average就是排队结账的人数——包括正在结账的人和站在队里等的人甚至包括那些“被卡在厕所里出不来”的人对应不可中断睡眠状态后文会细说。怎么判断负载高不高很多人说“load超过1就是有问题”这句话要打一个很大的折扣。正确的判断标准是和CPU核数挂钩load小于核数是正常等于核数是满载超过核数才是排队了。一台4核机器load4说明每个核都在满负荷跑一台2核机器load4那就意味着平均每个核有两个任务在排队。三个数值放在一起看也有讲究——1分钟值 15分钟值说明负载正在上升这是刚刚出现了什么突发情况反之如果1分钟值 15分钟值说明高峰正在过去。注意这不是绝对真理但作为第一直觉判断非常管用。我在实际中经常遇到的情况是1分钟load很高、15分钟load很低这时候基本都是刚出了一个瞬时高峰比如某个定时任务开始跑了、有人在上线发布如果5分钟和1分钟值也齐刷刷涨上去了那就要认真排查了。2.2 CPU行13个子字段逐一拆解第二行Tasks统计了进程总数和四个状态的数量这块放到后面讲进程状态时一起说。先看第三行——%Cpu(s)这一行才是判断CPU瓶颈的关键。标准输出把CPU时间分成了8个子项字段含义解读要点us用户态CPU时间占比跑应用程序nginx、java、python等消耗的CPU这个值高通常说明是业务代码在干活sy内核态CPU时间占比系统调用、内核线程、设备驱动消耗的CPU这个值高要么是系统调用太频繁要么有内核态瓶颈ni被nice调整过优先级的进程消耗的CPU只有当进程的nice值不是0时它消耗的CPU才会算进niid空闲CPU占比反过来理解就是“没被用掉的CPU”wa等待I/O完成所消耗的CPU占比这个值高说明CPU在干等着磁盘/网络完成读写进程并没有主动让出CPU而是卡在了I/O上hi硬中断消耗硬件设备网卡、磁盘控制器发起的中断si软中断消耗内核线程处理软中断的时间网络收发量大时这个值会明显升高st虚拟机被偷走的时间物理宿主机把分配给这个虚拟机的CPU时间拿去给别的虚拟机用了只有在虚拟机里才能看到非零值很多新手第一眼看到id很高比如90%以上就觉得“机器没问题”。但在实际排障中wa高才是最容易误判的场景当CPU密集型的任务很少、但I/O等待很高的时候us和sy都不高id也不高反而是wa占了很大一块。这时候CPU本身没有被打满但系统整体响应就是慢因为大家都在等磁盘。st这个值云服务器上很常见。如果你发现一台云主机CPU占用不高、load却上去了看一眼st——如果st占了10%以上很大概率是宿主机上的其他虚拟机在抢资源这种情况你在自己机器上怎么优化都没用只能换实例规格或者换物理机。2.3 Mem和Swap行的计算逻辑top输出里的内存行和free命令的展示逻辑略有不同看错了容易闹出“明明内存还有很多却以为不足”的笑话。top里这一行是KiB Mem : 3882420 total, 593920 free, 1428424 used, 1860076 buff/cache这里有个最常见的误区只看free和used觉得free只有593MBused却有1.4GB以为内存马上要爆了。实际上Linux内核对内存的使用策略是“能用则用”——那1.86GB的buff/cache是磁盘页缓存是内核拿空闲内存去缓存文件数据目的是加速磁盘读取。当应用程序真正需要内存时这部分缓存可以被回收。所以真正判断内存压力的指标是avail Memavailable memory即可用内存。这个值在Swap行的末尾它估算的是“在不触发swap的情况下还能分配多少内存给应用程序”。在我前面贴的输出里avail Mem是2,029,244 KiB约2GB说明这台机器内存其实相当宽裕完全不用因为free只有593MB而慌。再补充一个不太容易注意到的地方如果swap的used从0慢慢往上涨说明物理内存确实顶不住了内存页开始被换到磁盘上。一次两次还好如果swap的used持续高位且wa同步升高大概率就是内存不足引起的性能恶化。这时候就算CPU是100%空闲业务该卡还是卡。3. 进程列表区每一个字段都不是摆设含VIRT、RES、%CPU的底层逻辑3.1 进程列表字段速查表top输出的下半部分是一张进程列表默认按%CPU降序排列。每一列的完整含义如下字段含义我的判断习惯PID进程ID杀进程前先看清楚别误杀systemdUSER运行进程的用户看到root用户跑了个莫名进程心里先打个问号PR内核视角的进程优先级数字越小优先级越高范围通常-20到20普通进程一般是20NI用户视角的nice值可以手动调整范围-20到19调低负数等于给进程“加优先级”VIRT进程虚拟内存总大小包含未实际分配的部分参考价值有限RES常驻物理内存大小这是进程真正占用的物理内存排查内存问题看它SHR共享内存大小包括共享库、共享内存段等不代表独享内存S进程状态R运行、S睡眠、D不可中断睡眠、Z僵尸、T停止%CPUCPU使用率注意是相对单个核的百分比多核可以超过100%%MEM物理内存占用率RES/total的百分比TIME进程累计占用的CPU时间这是一个极其重要的指标很多人忽略它COMMAND命令名/进程名用c可以切换显示完整命令行3.2 VIRT、RES、SHR到底差在哪很多人在top里第一眼会被VIRT吓到一个Java进程VIRT能到十几GBRES才1GB是不是泄漏了不是的。VIRT包含的是进程的整个虚拟地址空间——所有映射进来的共享库、内存映射文件、还没实际分配的堆空间都会算进去。判断内存泄漏不能只看VIRT变大重点要看RES是否在持续增长。RES才是进程真正占用的物理内存。但RES里也包含了一部分SHR共享内存比如动态链接库libc.so所有进程共享它的一份物理页每个进程的RES里都算了一份。所以严格地说如果你把所有进程的RES加起来会大于系统的实际物理内存总量——这个“重复计算”的误差就来自SHR。实际排障中我判断某个进程内存是不是紧张基本只看RESRES持续增长、到Swap used也在涨那基本就是内存问题了。另外补充一个技巧在top里按一下小写x可以高亮当前排序列再按M按内存排序看看谁在最上面就可以快速定位内存大户。3.3 %CPU的采样陷阱和TIME的真实价值这是top命令最容易被误解的地方也是我认为最值得单独拿出来讲的一个点。%CPU这一列显示的是进程在最近一个刷新周期内的CPU占用统计。top默认每3秒刷新一次它取的是从上次刷新到本次刷新之间这段窗口的采样数据。问题在于一个进程如果有多个线程它显示的是所有线程占用CPU的总和这个总和可以超过100%比如4个线程每个跑满一个核%CPU就会显示400%。那“%CPU100%”到底代表什么它代表在采样窗口内这个进程平均占满了1个CPU核。注意“平均”这个词——top不是实时计数器它只是一个低频采样器。你看到%CPU100%的时候实际可能是这个进程在一瞬间冲到了800%然后在其他时间休眠把窗口内的均值拉到了100%。反过来同理一个周期性突发任务的%CPU可能显示很低但实际它的瞬时峰值非常高。所以我在排查CPU问题时从来不只看%CPU而是结合TIME一起看。TIME是进程从启动到现在累计消耗的CPU时间格式是“分:秒.百分秒”。这个值才是硬指标——它不会被采样窗口稀释是内核实实在在记账的。看到一个进程%CPU只有30%但TIME已经有几百分钟了而且这个数字还在稳步变大那它才是真正一直在吃CPU的那个。想快速看累计CPU时间在top里按A可以按TIME排序。另外%CPU还有一个更深的坑top的采样频率远低于进程的实际状态切换频率。内核的调度器每秒钟会发生无数次状态切换top只是隔几秒取个快照。如果嫌默认3秒精度不够按s再输入一个更小的刷新间隔比如0.5秒但间隔设太短会消耗额外的CPU生产环境不建议低于1秒。3.4 进程状态的隐藏信号D、S、R、Z进程状态列S往往是排障里最有信息量的一列也是最容易被扫一眼就忽略的一列。Rrunning正在运行或可运行。注意只要在运行队列里排着等CPU即使没拿到CPU时间片状态也是R。所以R状态的进程多不代表CPU忙也可能只是排队而已。Ssleeping可中断睡眠。进程在等某个事件比如网络包、定时器时进入这个状态这是完全正常的。Duninterruptible sleep不可中断睡眠也叫TASK_UNINTERRUPTIBLE。这是排障时最值得警觉的状态。进程卡在磁盘I/O或者其他不可中断的内核操作里出不来你kill都kill不掉。D状态进程多了load average会被拉高因为load统计的就是R和D两个状态的进程数。这时候你再回头对照CPU行的wa——如果wa也高基本可以锁定是I/O层面的瓶颈。Zzombie僵尸进程。进程结束了但父进程没调用wait()来回收它的退出状态它就变成僵尸。僵尸本身不消耗资源但如果批量堆积说明父进程出了毛病没处理好子进程退出需要排查父进程逻辑。我在一次线上排障时就遇到过D状态的坑数据库备份脚本在凌晨2点触发全量备份磁盘本身是机械盘I/O能力有限瞬间几十个D状态进程出现load直接从0.5飙到15但CPU的us和sy都不高id也还有30%多。如果只看CPU不看D状态和wa很容易被带偏。4. top跑起来之后交互式按键与定位问题的实战动作4.1 常用按键速查表top不是只能看默认界面的它有一整套交互式按键。我把最常用的几个列出来每个都值得养成肌肉记忆按键作用我的使用场景1展开/折叠每个CPU核的独立统计多核机器看是不是某个核被单线程打满P按%CPU降序排列默认就是这个但切过别的排序后按它切回来M按RES内存降序排列排查内存占用时用T按TIME累计CPU时间排序找出长期吃CPU的“隐形大户”H切换线程视图/进程视图排查多线程应用Java、Nginx worker时必用c切换完整命令行/短命令名看脚本跑的是哪个参数x高亮当前排序列配合排序键用一目了然d/s修改刷新间隔秒默认3秒需要精细观察时改成1秒u按用户名过滤只看某个用户的进程k发送信号给进程相当于kill但建议还是到另一个终端里kill免得误操作q退出你懂的4.2 实战定位问题的操作路径我排CPU问题的标准操作流程是第一步进到top里先看load的三个值和%Cpu(s)行。如果us高说明业务代码在跑如果sy高说明要么系统调用太频繁要么某个驱动在内核态死循环如果wa高转向磁盘I/O方向排查。第二步按1看每个核的分布。这里有个特别典型的情况如果机器是32核你看到%CPU总占用只有6%但1展开后发现第7个核是100%其他核都是0%——这通常是单线程程序的问题说明应用本身只能用一个核。这时候你去优化业务代码并发度才有意义盲目加机器反而是浪费钱。第三步按P看%CPU最高的进程记下PID再看一眼TIME。如果%CPU最高的是top自己那说明你的机器其实挺闲的别被吓到。第四步如果涉及多线程应用按H切到线程视图。我处理过一个Java服务CPU持续135%的问题进程视图里根本看不出端倪——因为12个线程分摊了这135%每个线程也就是十几%。切到线程视图后发现有个线程长期占100%再用printf %x\n 线程PID转成十六进制用jstack导出的线程dump一匹配直接定位到是Gson序列化那段代码里的死循环。这个流程我愿称之为“top -H jstack”组合拳Java排查CPU问题的效率比瞎猜高十倍。4.3 用批处理模式做数据采样top还有一个非常适合脚本化的模式批处理模式。top -b -n 2 -d 3-bbatch表示以非交互模式输出-n 2表示采样2次后退出-d 3表示间隔3秒。为什么要-n 2而不是-n 1因为top的第一次采样显示的是从进程启动到第一次采样之间的累计数据不完全是当前瞬时状态第二次采样才是真正可比的快照。写监控脚本的时候这一点很重要只采一次的话你会看到一些奇怪的瞬时值。在实际工作中我经常这样用怀疑某段时间CPU异常但人不可能24小时盯着屏幕就让crontab每5分钟跑一次top -b -n 1 -p PID把结果追加到日志文件事后回头看趋势。还有一个进阶玩法top -b -n 1 | awk NR8 {print $1, $9, $12}之类的组合取PID、%CPU、COMMAND三列做简单统计。但注意批处理模式默认输出格式和交互模式一样字段位置是固定的用awk按列切分之前先确认一下你的top版本输出的列顺序。我踩过一个坑在Ubuntu上用top输出做监控脚本结果某次升级后COMMAND列从12列变成了11列启用了完整命令行显示awk切出来全乱了后来统一改成top -b -n 1 -c并明确列位置才稳定下来。5. 一次load飙高的完整排查链路实战复盘理论说再多不如带着走一遍真实场景。下面是我前段时间处理过的一次线上问题过程很有代表性。5.1 现象与初步判断告警群里弹出一条消息某台4核应用服务器的load average连续5分钟超过8。登录后第一件事就是敲top当时的输出大致是top - 10:32:18 up 12 days, 6:10, 1 user, load average: 8.53, 6.21, 3.85 %Cpu(s): 12.5 us, 3.2 sy, 0.0 ni, 18.5 id, 62.3 wa, 0.0 hi, 3.5 si, 0.0 st看到这个输出我脑子里的第一反应是load已经8.5这么高了但CPU真正在干活的只有us 12.5% sy 3.2% ≈ 16%id还有18.5%剩下62.3%全在wa。这说明CPU并没有被打满而是大量时间花在等I/O上。load之所以冲到8.5是因为load统计里包含了不可中断睡眠D状态的进程而D状态进程恰恰就是卡在I/O上的那些进程。这是一个非常典型的“假CPU繁忙真I/O瓶颈”的组合。如果当时我只盯着us会觉得CPU还行从而漏掉真正的瓶颈点。5.2 结合top参数逐层缩小范围接下来按1看各核分布结果4个核wa都高说明不是单个磁盘分区的问题而是整机I/O都在堵。按M按内存排序RES总量才用了不到2GB内存不是瓶颈排除内存不足导致swap换页的可能。然后按c看完整命令行我想知道到底是谁在发起大量I/O。排序切回P%CPU高一点的是一个Java应用正常业务和一个mysqld进程。乍一看都不算异常。但这时我做了个关键动作按H切到线程视图发现mysqld下面有十几条线程状态是D且都挂在同一条TID上隐隐约约对应同一个I/O操作。再配合wa高这个事实基本可以断定是MySQL在大量读写磁盘。紧接着确认一下是不是慢日志或者临时表落盘的问题。在MySQL端查SHOW PROCESSLIST发现大量Copying to tmp table on disk的会话——一些排序、分组操作把临时结果集写到了磁盘导致瞬间来了十几张临时表的磁盘写入。这就是wa飙升、D状态进程增加、load冲到8以上的直接原因。5.3 根因定位与处理根因往深挖一步为什么突然会有那么多需要落盘的临时表进一步排查发现是一条新上线的报表SQL关联了6张表ORDER BY的字段不在索引里导致MySQL只能把中间结果集写到磁盘做filesort。SQL是凌晨发布上线的所以load是早上才开始涨。处理过程分三步第一步先在MySQL端把那几条慢查询的会话kill掉I/O压力立刻下来load在几分钟内回落到1以内。第二步让开发优化那条SQL——加联合索引减少一次子查询临时表从磁盘改到内存调大tmp_table_size和max_heap_table_size。第三步复盘时给数据库所在的磁盘监控加了iostat告警以后wa一高就通知而不是等load告警。整个排障过程从头到尾最核心的工具就是top。它给的直接信息是表象——load高、CPU状态分布异常、D状态进程多、wa高——但顺着这些线索逐层展开才能一步步从“load高”走到“SQL需要优化”这个根因。6. 新手最容易误判的几个top输出细节6.1 %CPU高不等于CPU被打满前面讲过%CPU是相对单个核的百分比。一个进程%CPU显示300%只说明它吃了3个核的算力不代表系统整体CPU已经到极限。反过来也一样%CPU显示0.5%不代表这进程对系统没有压力——可能是它主要在等I/O比如D状态CPU根本没轮到用。判断系统CPU整体是否紧张永远优先看%Cpu(s)这一行的us和sy之和而不是盯着进程列表里的%CPU最大值。ussy持续接近100%才是真正的CPU饱和信号。6.2 free内存小不等于内存不足这是我从入行就听人讲错的常见误判。看到free只有几百MB就急着说要加内存其实只要avail Mem还有宽裕系统运行不会有任何问题buff/cache会在应用需要时让出内存。真正需要警惕的是avail Mem持续走低、且Swap的used在涨那才是内存压力。另外如果看到buff/cache特别大但可以回收cached占大头也不必担心这是Linux的正常行为。6.3 load高但CPU不高时先看wa和D状态我遇到过不止一次初学者拿着高load的截图来问“CPU才20%为什么load有10”。如果你已经看懂了前面的内容现在应该能自己回答load统计的是等待调度的进程数而D状态进程是“卡在I/O里出不来”的它们也算在load里。所以load高CPU低大概率是I/O瓶颈或者有大量D状态线程跟CPU没关系。判断I/O瓶颈光看top里的wa还不够建议配合iostat看%util和await、iotop看哪个进程在读写。top给你的是“是不是I/O的问题”iostat给你的是“I/O设备压力有多大”iotop告诉你“谁在制造压力”三家配合才是完整的I/O排障流程。6.4 top的默认视图看不到的东西top默认不显示进程PPID、不显示磁盘I/O速率、不显示每个线程的单独CPU时间除非切到线程模式、也不区分用户态和内核态的CPU时间中到底谁占大头。这些信息在某些场景下非常关键想看进程的启动时间或父进程按f进字段管理界面勾选PPID、START等列再按空格选中回车保存。想看磁盘整体的实时吞吐和IOPS不要指望top直接iostat -x 1。想看网络连接和带宽占用top也完全帮不上忙需要iftop或nethogs。我自己的习惯是top永远作为第一落点因为它最快、最全面能在3秒内告诉你“是CPU、内存还是I/O的方向有问题”但top给不了全部答案拿到方向之后马上切换到针对性的工具深挖。配合top时还有两个小经验值得分享。一个是在排查“CPU告警”的时候保留top的实时输出和当时的业务日志把时间点对齐很多问题只有把系统和业务时间线对齐才能定位。另一个是生产环境不要随手就按s改成0.1秒刷新——top本身也会消耗CPU间隔太短会把本来就不宽裕的CPU变得更紧张也容易让监控数据失真。top这个命令入门门槛很低但要真正能用好它得把每个参数的底层逻辑吃透。踩过几次坑之后再回头看你会发现它其实是Linux性能排查里性价比最高的一条命令——只要读懂了那张表你就已经排除了至少一半的错误方向。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →