top —— 实时进程/系统资源总览
这个工具是做什么的
最常用的入门工具,一屏看全整机和各进程的 CPU、内存占用,是排查的"第一现场"。适合快速定位"哪个进程/线程在耗资源、耗的是哪一类 CPU",但要落到函数级还得靠 perf。
可以回答什么问题
| 问题 | 怎么用 |
|---|---|
| 现在系统整体忙不忙?负载趋势如何? | 看顶部 load average 和 %Cpu(s) 行 |
| CPU 主要耗在用户态还是内核态?有没有在等 IO? | %Cpu(s) 行:us/sy/wa 三个核心指标 |
| 哪个进程在吃 CPU / 内存? | 进程列表区默认按 %CPU 排序,按 M 切到内存排序 |
| 多线程应用中哪个线程在烧 CPU? | top -H -p <PID> 展开线程视图 |
| 负载压在单个核心还是均匀分布? | 按 1 把 %Cpu(s) 展开成每个核心一行 |
| 有没有僵尸进程? | 第 2 行 zombie 字段 |
数据来源
- 来源文件:整机 CPU 取自
/proc/stat(各类 jiffies);每个进程/线程取自/proc/<pid>/stat、/proc/<pid>/status(utime/stime、内存、状态);内存汇总取自/proc/meminfo;负载取自/proc/loadavg。 - 采集方式:每个刷新周期把这些文件读一遍,与上一次的累计值做差,再除以间隔时间算出
%CPU、%MEM等瞬时百分比。 - 由此决定的特性:因为要"两次采样做差",刚启动的第一屏
%CPU是拿开机以来的累计值算的、通常不准,看第二次刷新起的数值才对;这也是所有"读 /proc 做差"类工具(pidstat/vmstat/iostat/mpstat)的共性。
一、启动参数
top # 实时刷新(默认 3 秒一屏)
top -d 1 # -d 指定刷新间隔(秒),可用小数 top -d 0.5
top -p <PID> # 只监控指定进程,逗号分隔多个:top -p 1234,5678
top -H # 线程模式:把线程当独立行显示
top -H -p <PID> # 只看某进程的各线程(多线程排查必用)
top -u <user> # 只看某用户的进程
top -c # 显示完整命令行(而不仅是进程名),便于区分同名进程
top -b -n 1 # 批处理模式跑 1 次(-n 指定次数),配合重定向存快照/给脚本解析
top -b -d 5 -n 12 > top.log # 每 5 秒一次共 12 次,采集 1 分钟写日志
top -o %MEM # 启动即按指定列排序(新版支持)二、读懂输出的两大部分
1) 顶部汇总区(5 行)
top - 15:04:21 up 12 days, 3:11, 2 users, load average: 1.25, 0.98, 0.75
Tasks: 218 total, 2 running, 216 sleeping, 0 stopped, 0 zombie
%Cpu(s): 6.3 us, 2.1 sy, 0.0 ni, 90.8 id, 0.5 wa, 0.0 hi, 0.3 si, 0.0 st
MiB Mem : 15889.2 total, 1024.5 free, 4820.1 used, 10044.6 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 10600.3 avail Mem第 1 行——load average: 1.25, 0.98, 0.75 是过去 1/5/15 分钟的平均运行队列长度。经验判读:把它和 CPU 核数比,load ≈ 核数 表示满载,load > 核数 表示有任务在排队等 CPU(或等 D 状态 IO)。三个数看趋势:递增=负载在涨,递减=高峰已过。
第 2 行——running 是正在运行/可运行数;zombie 非 0 说明有子进程退出后父进程没回收(wait),需查父进程。
第 3 行 %Cpu(s) 是排查 CPU 类型的核心:
| 字段 | 全称 | 含义与判读 |
|---|---|---|
us | user | 用户态:业务代码、纯计算。高 → 看 perf 用户态函数 |
sy | system | 内核态:系统调用、内核逻辑。高 → 频繁 syscall/锁/缺页,用 strace/perf |
ni | nice | 被 nice 调过优先级的用户态时间 |
| id | idle | 空闲 |
wa | iowait | CPU 空闲且在等磁盘/网络 IO。高 → IO 瓶颈,转 iostat/iotop |
hi | hardirq | 硬中断(网卡/磁盘控制器等硬件触发) |
si | softirq | 软中断。高 → 网络小包风暴、网卡收发,转 mpstat -P ALL 看是否集中单核 |
st | steal | 虚拟机被宿主偷走的时间。高 → 宿主超卖,同机其他 VM 在抢 CPU |
按
1可把这一行展开成每个逻辑核一行,判断负载是均匀还是压在单核。按t切换这行的呈现样式(数字/进度条)。
第 4/5 行内存:重点看 buff/cache(可回收的缓存,不算真占用)和第 5 行末的 avail Mem(真正可用内存)。判断内存紧不紧只看 avail Mem,别被 free 小吓到。
2) 进程/线程列表区
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8921 derek 20 0 812400 45120 3200 R 99.7 0.3 1:04.55 cpu_demo| 列 | 含义与判读 |
|---|---|
PID | 进程号(-H 下这里是线程 TID) |
PR/NI | 内核优先级 / nice 值(-20~19,越小越优先) |
VIRT | 虚拟内存总量(含未真正分配的映射,通常偏大,别当真实占用) |
RES | 常驻物理内存(真正占的 RAM,看内存就看它) |
SHR | 共享内存(共享库等) |
S | 进程状态:R 运行、S 可中断睡眠、D 不可中断睡眠(几乎都是卡在磁盘/网络 IO,D 多 + load 高 = IO 拖累)、T 暂停、Z 僵尸 |
%CPU | 用户+内核 CPU 之和。默认按整机单核 100% 计,多核跑满多个线程可 >100%(如 8 核满载 = 800%) |
%MEM | RES 占物理内存比例 |
TIME+ | 累计占用 CPU 时间(百分之一秒精度),进程存活期总账 |
COMMAND | 进程名(-c 显示完整命令行) |
三、交互快捷键(运行中直接按)
| 键 | 作用 |
|---|---|
P | 按 %CPU 排序(默认) |
M | 按 %MEM 排序 |
T | 按累计 TIME+ 排序 |
H | 切换线程视图(等价 -H) |
0 | 零值抑制开关:把值为 0 的字段显示出来 / 隐藏为空白(不是 CPU 相关,别和下面 1/2/3 混) |
1 | %Cpu 行:单行汇总 ↔ 每个逻辑核一行,来回切换 |
2 | 按 NUMA 节点汇总显示 CPU(仅 NUMA 机器有效) |
3 | 展开指定某个 NUMA 节点:按下后输入节点号,只看该节点的核 |
t/m | 切换 CPU 行 / 内存行的显示样式(数字↔进度条) |
c | 切换显示完整命令行 |
f | 字段管理器:空格增删列、s 设排序列(可加 nTH 线程数、P最后运行核、SWAP) |
x/y | 高亮排序列 / 高亮运行中的行 |
k | 输入 PID 杀进程 |
r | renice 改优先级 |
u | 只看指定用户 |
V | 树状视图(看父子进程关系) |
W | 把当前配置存到 ~/.toprc,下次自动生效 |
q | 退出 |
CPU 汇总/NUMA 显示键 0 1 2 3 详解
这几个键常被一起问,但 0 和 1/2/3 其实是两回事:
| 键 | 作用 | 说明 |
|---|---|---|
0 | 零值抑制(Zero Suppress) | 切换"是否把值为 0 的字段显示为空白"。和 CPU 显示无关,纯粹让满屏的 0 变干净,看非零数据更省眼。 |
1 | 单行汇总 ↔ 逐核 | 默认 %Cpu(s) 是所有核的一行平均;按一下变成 %Cpu0 / %Cpu1 / ... 每核一行,再按回到汇总。判断"负载是均匀还是压在单核"最常用。 |
2 | 按 NUMA 节点汇总 | 在多路 NUMA 机器上,按节点(node)分组显示各节点的 CPU 汇总,看负载是否跨节点倾斜。非 NUMA 机器按了会提示不可用。 |
3 | 展开指定 NUMA 节点 | 按下后提示输入节点号,只显示该节点内各核的明细,用于深入某一个节点。 |
记忆:
1= 逐核,2= 按 NUMA 节点,3= 单个 NUMA 节点,三者是"CPU 展开粒度"从细到聚焦;0是独立的零值显示开关。cpu_demo 场景1(单线程死循环)按1展开,会看到只有一个核满、其余空闲。
2/3涉及的 NUMA 概念(节点、本地/远程内存访问)见 numa。
四、结合本仓库实操
make && ./cpu_demo # 另开终端运行靶子,按提示选 1/2/3
# 观测终端:
top -H -p $(pgrep cpu_demo)- 选 场景 1(用户态死循环):
%Cpu(s)的us明显上升;-H展开可见负载压在那个 detach 出来的work_thread线程上,主线程几乎空闲。 - 选 场景 2(频繁 open/write/close):
sy上升(系统调用多),可能伴随少量wa。 - 按
1看逐核:场景 1 是单线程,只有一个核被打满,其余空闲——直观说明"加核无用,得优化代码或并行化"。
五、关键指标速查
| 指标 | 缩写/英文 | 正常值 | 异常信号 |
|---|---|---|---|
| 系统负载 | LA / Load Average | LA ≈ CPU 核数 | LA 持续 > 核数 → 过载,任务排队 |
| 负载趋势 | 1min/5min/15min 对比 | 三者接近 | 1min 远大于另两个 → 负载在暴涨 |
| 用户态 CPU | us / %User | 视业务 | 单核 100% 其余空闲 → 单线程瓶颈 |
| 内核态 CPU | sy / %System | < 10% | > 30% → 频繁 syscall/锁/缺页,用 strace 排查 |
| IO 等待 | wa / %iowait | < 5% | > 20% → IO 瓶颈,转 iostat |
| 硬中断 | hi / %HardIRQ | < 1% | 异常高 → 中断风暴 |
| 软中断 | si / %SoftIRQ | 各核均匀 | 集中在单核 → 网卡多队列未开 |
| 被偷 CPU | st / %Steal | 0% | > 5% → 云主机超卖 |
| 空闲率 | id / %Idle | > 10% | 长期 → 0 → CPU 打满 |
| 常驻内存 | RES / Resident Memory | 视业务 | 持续单调增长 → 内存泄漏 |
| 进程状态 | S / State | R 或 S 为主 | D 状态多 → IO 阻塞;Z → 僵尸进程 |
| 运行任务数 | running | 视业务 | 持续 > 核数 → CPU 过载 |
| 僵尸进程 | zombie | 0 | > 0 → 父进程未 wait() 回收子进程 |
六、常见排查方法与分析
6.1 CPU 利用率高 → 定位到线程/函数
top → 按 P 排序找高 CPU 进程
→ top -H -p <PID> 展开线程
→ 找到高 CPU 的 TID
→ perf top -t <TID> 看热点函数
→ 若 sy 高 → strace -c -p <PID> 看系统调用6.2 负载高但 CPU us/sy 都不高
可能原因:大量 D 状态进程卡 IO。确认方法:
top → 看 %Cpu 行 wa 是否高
→ 看进程列表 S 列是否有 D 状态
→ iostat -x 1 确认磁盘瓶颈
→ iotop -o 找 IO 大户6.3 内存泄漏嫌疑
top → 按 M 排序
→ 持续观察 RES 列是否只增不减
→ pidstat -r -p <PID> 1 追踪 RSS 趋势
→ cat /proc/<PID>/status | grep VmRSS 看增长速率6.4 单核打满 vs 多核均匀
top → 按 1 展开逐核 %Cpu
→ 只有一个核满 → 单线程瓶颈(加核无用,需并行化)
→ 多个核满 → 多线程利用率好,若仍不够需加核或优化算法七、交叉引用
- 逐核分析:mpstat(更精确的逐核 + 中断分布)
- 进程级 CPU 拆分:pidstat(按 utime/stime 拆分,可下钻到线程)
- 调度观测:scheduling-observation(从负载到上下文切换的完整分析)
- 函数级热点:perf(perf top/record 定位到函数和代码行)
- 系统调用追踪:strace(sy 高时定位具体 syscall)
- 内存详解:free(available 才是真正可用内存)
- 磁盘 IO:iostat(wa 高时进一步定位)
- /proc 数据源:procfs(top 的数据怎么来的)
- 场景化排查链路:排查工具集(CPU 高 → 内存紧 → IO 慢 → 网络卡 → 进程崩,五条可一键走通的链路)
八、常见坑
%CPU加起来超过 100%:正常,多核按核累加。想看归一化到整机的百分比,f里换用 Irix/Solaris 模式(按I切换:Irix 关掉后%CPU会除以核数)。- 只看进程会漏掉线程问题:多线程程序不加
-H,只能看到进程聚合值,定位不到具体线程。 - top 自身也耗 CPU:刷新间隔太小(如
-d 0.1)在进程数多时 top 自己就很吃 CPU,观测会失真。 - 想要更友好的界面用 htop(彩色、鼠标、可横向滚动、树视图),字段含义与 top 一致。
总结
top 是定位资源消耗的第一现场:每屏读 /proc/stat 和 /proc/[pid] 两次做差,给出负载趋势、us/sy/wa 等 CPU 类型和按 %CPU/%MEM 排序的进程,-H 下钻线程、1 展开逐核可快速区分单线程瓶颈(加核无用)与均匀多线程负载;但第一屏基于开机累计值不准,要从第二次刷新起读数。