尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

CentOS 7.6上用perf top精准定位CPU热点函数

发布时间:2026/9/24 19:21:41

资讯中心
01
ARTICLE

CentOS 7.6上用perf top精准定位CPU热点函数

CentOS 7.6上用perf top精准定位CPU热点函数
最近接手一台 CentOS 7.6 的机器用户反馈 CPU 使用率莫名飙到 90% 以上top 一看进程是 java但具体是 JVM 里哪段代码在疯狂占资源完全看不出来。这种时候我最先想到的工具就是 perf top它能在不重启进程、不侵入代码的前提下直接告诉我们 CPU 到底在执行什么函数。这篇文章就把我在 CentOS 7.6 上使用 perf top 排查性能问题的方法完整记录下来从安装、界面拆解到实战案例希望给同样被性能问题折磨的运维和开发同学一些参考。perf top 是 Linux 性能剖析工具集 perf 里的一个交互式命令作用和 top 类似但 top 展示的是进程级别perf top 展示的是函数级别。它能解决什么问题一句话当你知道机器 CPU 高但不知道高在哪里时perf top 可以直接定位到热点函数帮你找到性能瓶颈。适合谁看需要排查线上问题、做性能调优的后端开发、运维工程师以及想深入理解 Linux 内核和程序执行原理的爱好者。1. perf top 的工作原理与核心设计拆解1.1 采样机制为什么它能看到函数级别而不是进程级别perf top 底层的核心机制是采样具体来说是基于 PMUPerformance Monitoring Unit性能监控单元硬件的周期性中断采样。它不像 strace 那样拦截系统调用也不像 gprof 那样需要在编译时插桩而是利用 CPU 硬件自带的能力每隔固定时间可以通过 -F 参数指定频率触发一次中断在中断处理函数中记录当前 CPU 正在执行的地址。这个地址拿到手之后perf top 会把地址翻译成具体的函数符号。如果是用户态程序会通过 /proc/ /maps 和 ELF 文件里的符号表来解析如果是内核态就通过 /proc/kallsyms 来解析。翻译完成之后perf 会按函数符号进行计数聚合最后在界面上按照采样命中的次数从高到低排列展示。这里有个非常重要的概念要理解perf top 是采样不是全量追踪。它的统计结果是一个估算采样频率越高、运行时间越长估算越准确。默认情况下 perf top 的采样频率是 4000Hz也就是每秒中断 4000 次这对于定位热点函数来说基本够用。我见过很多刚接触 perf 的同学把采样和 trace 混为一谈其实两者的精度和开销完全不同。perf top 因为靠硬件中断驱动本身开销极低实测中一般不会超过 2%~3% 的 CPU 占用这是它适合在生产环境临时排查问题的关键原因。1.2 用户态与内核态perf top 眼中的两个世界perf top 显示的结果里你会看到两类符号一类是内核符号比如 native_write_msr、menu_select、do_syscall_64 这些它们属于内核态另一类是用户态程序的符号比如 libc 里的 pthread_mutex_lock、JVM 里的 CompiledMethod或者你自己写的 C/C 函数名。区分用户态和内核态很重要因为排查问题的方向完全不同。如果热点集中在内核符号上可能说明系统调用频繁、中断处理过量、内存回收压力大如果热点集中在用户态符号上那就要重点看应用代码本身的逻辑。perf top 在界面上默认用 [kernel.kallsyms] 标注内核符号的归属模块用户态程序则直接显示可执行文件名或者共享库名。这里有一个常见的坑如果你在虚拟机里跑 perf top看到的可能大量是 hypervisor 相关的符号因为虚拟 CPU 的中断处理路径会经过宿主机。我在 VMware 虚拟机上就遇到过这种情况当时花了半天排查业务代码最后发现热点全在 hypervisor 的 mmio 相关函数上。遇到这种场景优先用 -p 参数指定进程或者直接看用户态符号占比。2. CentOS 7.6 环境下的安装与准备工作2.1 安装 perf一条命令的事但版本要选对CentOS 7.6 默认的软件源里自带 perf 工具包包名和内核版本绑定。直接执行yum install -y perf装完之后执行 perf top正常情况下就能看到采样界面。但这里有一个非常容易踩的坑perf 的版本必须和当前内核版本严格对应否则会出现无法解析内核符号或者直接报错的情况。CentOS 7.6 的内核版本一般是 3.10.0-957.el7所以安装时尽量确保perf --version输出的版本信息里包含同样的内核版本号。如果系统里有多套内核比如升级过内核一定要在启动菜单里确认当前跑的是哪个内核版本。我曾经在一台机器上发现 perf top 能跑但采样结果全是未知符号排查了半天最后发现是之前运维升级内核后没有重启导致用户态的 perf 工具版本和新内核的 PMU 接口不兼容。这种问题在 CentOS 上不算少见因为 yum 安装的 perf 是和内核 RPM 打包在一起的内核升级后如果不重启perf 工具和运行中的内核就不是同一套代码了。如果 yum 装不上也可以用内核源码里自带的 perf 编译。CentOS 7.6 的源码包地址在 vault.centos.org 上能找到但说实话没必要yum 源里已经集成了直接装就好除非你需要打特定的补丁。2.2 内核符号表看不到函数名的关键所在刚才提到 perf top 需要解析内核符号这个依赖是 /proc/kallsyms。很多精简安装的 CentOS 7.6 默认没有完全开放内核符号你会看到[kernel.kallsyms]下全是unknown之类的符号根本没法用。解决方法是安装内核调试符号包yum install -y kernel-debuginfo-$(uname -r)不过这个包在默认的 Base 源里没有需要先安装 EPEL 和 debuginfo 源。实际操作中我一般这样配yum install -y epel-release yum --enablerepobase-debuginfo install -y kernel-debuginfo-$(uname -r)需要注意的是kernel-debuginfo 包比较大一般有几百 MB下载时间取决于网速。而且它严格对应内核版本和架构如果内核是 3.10.0-957.el7.x86_64就必须找同名同架构的 debuginfo 包错一个字符都装不上。还有一种情况是权限限制。查看 /proc/kallsyms 需要 root 权限普通用户执行 perf top 时即使装了 debuginfo 也看不到内核符号。所以建议直接用 root 或者 sudo 执行。如果安全策略不允许 root 登录可以给特定用户配置 sudo 权限这是我在生产环境里常用的方式。3. perf top 界面逐字段拆解新手最需要理解的部分3.1 顶部概览和列字段含义perf top 启动之后终端会进入一个类似 top 的全屏交互界面。很多同学第一次看到这个界面会有点懵我先从上到下拆一遍。第一行是采样信息概览类似PerfTop: 8615 irqs/sec kernel:49.7% exact: 0.0% [4000Hz cycles], (all, 8 CPUs)。这里有几个关键信息8615 irqs/sec表示每秒采样中断次数这个数值一般等于采样频率乘以 CPU 核数kernel:49.7%表示采样到的样本里有 49.7% 落到了内核态[4000Hz cycles]表示当前采样事件是 CPU cyclesCPU 周期默认频率 4000Hz。接下来的列分别是Samples采样命中的次数这是排序的关键列越多说明该函数占用 CPU 时间越长。Shared Object该函数所属的模块内核态会显示 [kernel.kallsyms]用户态会显示具体可执行文件或 .so 库名。Symbol函数名。如果看不到函数名只有十六进制地址说明符号解析失败回到 2.2 节检查 debuginfo 和权限。理解这三列基本就能读懂 perf top 了。我一直强调perf top 的界面本质上就是在回答三个问题CPU 花在哪个模块Shared Object、花在哪个函数Symbol、花了多少比例Samples。3.2 交互操作切换排序、过滤和展开调用栈perf top 不是只读界面它支持一些交互操作这些操作在排查问题时非常实用。按E键可以选择采样事件默认是 cycles可以切换成 instructions、cache-misses、branch-misses 等硬件事件。比如你怀疑程序有严重的 cache 未命中问题切换成 cache-misses 之后热点函数分布可能完全不同这时候能定位到的是缓存使用不合理的代码段。按P键可以切换显示用户态符号还是内核态符号。这个功能在混合负载的场景下很有用比如一个 Java 应用加一个 Nginx你想分别看它们各自的热点而不是混在一起看。按F键可以输入过滤条件只显示包含特定字符串的符号。比如我只关心 Nginx 相关的函数就输入nginx界面就只显示匹配的符号了。按z键可以放大到当前选中的符号查看它在更细粒度上的调用情况。按g键可以查看某个符号的调用栈不过这个功能依赖内核栈的采样需要保证内核配置了 CONFIG_FRAME_POINTER 或者支持 ORC unwinder。CentOS 7.6 默认内核是支持基本调用栈采样的但如果你在虚拟机或者容器里跑有时会受限。我自己的使用习惯是先按E确认采样事件是 cycles然后按P看内核态/用户态的分布再结合F过滤关键的共享对象。这套组合拳做完95% 的问题都能有一个初步方向。3.3 数值精度和采样误差为什么数据会跳动perf top 实时刷新的数据会有肉眼可见的跳动这是正常现象不是工具坏了。因为采样本就具有随机性CPU 在采样瞬间执行到的指令具有偶然性。比如一个函数实际占用 CPU 20%但某一次采样周期里可能只命中了 15%下一次采样又变成 25%。如果要得到稳定的数据有几种做法。一是把采样周期拉长让数据自己趋于稳定二是用perf top --stdio模式跑固定时间后一次性输出统计结果这种模式适合脚本化采集三是使用perf record配合perf report用离线统计分析替代实时刷新精度更高后面我会专门讲这两个命令的搭配场景。这里补充一个很重要的概念perf top 显示的是 CPU 周期占比不是时间占比。CPU cycles 事件统计的是 CPU 时钟周期数它和程序执行时间是正相关的但受 CPU 频率动态调整Turbo Boost、节能模式的影响。如果你发现某个函数的周期数很高但它实际执行时间并不多要考虑是否和 CPU 频率波动有关。4. 实战演练用 perf top 定位一次 CPU 飙升问题4.1 第一步top 确认进程perf top 确认热点有一次线上环境出现 CPU 使用率告警我登录到机器做的第一件事是执行 top 查看进程状态。top 显示某个 Java 进程的 CPU 使用率一直在 300% 左右8 核机器正常应该在 50% 以下。确认了是 Java 进程之后我没有立刻用 jstack 抓线程栈而是先跑 perf top 看整体热点分布sudo perf top -p pid使用-p指定进程后perf top 就只看这个进程的采样结果了。执行之后映入眼帘的热点函数是[kernel.kallsyms]里的_raw_spin_unlock_irqrestore和futex_wake用户态区域则集中在libjvm.so的SpinPause和JVM_Sleep。这几个符号组合起来我的第一判断是JVM 内部发生了大量的线程竞争和锁等待也就是典型的锁竞争加剧导致上下文切换飙升的场景。为了验证这个判断我按E把采样事件切换到sched:sched_switch跟踪点需要 root 并配置好 tracing 权限观察线程切换的频率是否有明显异常。但跟踪点事件在 perf top 里不是默认就有的需要额外的权限配置所以我们先按住不表用默认的 cycles 事件继续分析。4.2 第二步按调用栈深入锁定到具体代码路径实时模式下SpinPause和futex_wake已经指出了大致方向但要定位到具体代码还需要看调用栈。perf top 默认不采集调用栈需要在启动时加-g参数sudo perf top -p pid -g加上-g之后按回车键展开当前选中的符号就能看到从用户态入口到内核态的完整调用链。我记得当时展开futex_wake时看到的调用链是pthread_cond_wait - __pthread_cond_wait_common - futex_wait - do_futex - futex_wake这基本坐实了是 Java 层条件变量等待被频繁唤醒。到这一步结合业务上下文我意识到是某个线程池的队列长度抖动导致线程频繁 sleep 和 wakeup。后来通过调整线程池核心线程数和队列容量解决CPU 占用从 300% 降到了 60% 左右。这个案例其实非常有代表性系统的性能瓶颈往往不会直接体现在业务代码函数名上而是会通过锁等待、线程切换等底层符号暴露出来。如果你只懂 JVM 而不懂 perf 的符号就很难把这些底层信号和业务层逻辑关联起来。4.3 第三步用火焰图辅助可视化perf top 是实时交互工具但它的信息呈现方式是列表不够直观。如果需要给团队展示或者做详细分析我更推荐配合火焰图。做法是用 perf record 采集数据然后生成火焰图sudo perf record -p pid -g -- sleep 30 sudo perf script out.perf然后把 out.perf 交给 FlameGraph 工具集里的 stackcollapse-perf.pl 和 flamegraph.pl 处理生成 SVG 火焰图。火焰图最直观的效果是横向越长表示占用 CPU 时间越多从上到下的调用栈关系一目了然。这里有个经验之谈perf record 采集 30 秒一般足够太短数据不充分太长会产生很大的 perf.data 文件。如果目的是定位 CPU 热点30 到 60 秒是比较合理的窗口。sleep 30是让 perf record 自动运行 30 秒后停止这样不用手动 CtrlC 中断非常适合脚本化采集。火焰图虽然好用但它不能替代 perf top。因为火焰图是离线生成的实时性差perf top 能让你在问题发生的当下立刻观察。所以我的习惯是双管齐下实时问题先用 perf top 快速定位需要做详细分析或者出报告时再用 perf record 生成火焰图。5. 常用参数详解与选型心得5.1 最常用的参数组合perf top 的参数很多但实际工作中常用的其实就那么几个。我整理了一张速查表参数作用使用场景-p pid指定要分析的进程明确知道问题进程时-F freq设置采样频率默认 4000HzCPU 频率过高导致开销大或过低导致采样不足-g采集调用栈需要定位函数调用链时-K/-U只采样内核态 / 只采样用户态区分负载类型时-C cpu指定采样的 CPU 核心排查绑核或中断亲和性问题时-E event指定采样事件需要分析 cache miss、分支预测等场景--stdio以非交互模式输出脚本采集和自动化巡检举例来说如果我要分析某个多线程 C 服务为什么 CPU 占用高但又不想被其他进程干扰我会这样执行sudo perf top -p $(pgrep -f my_service) -g -F 8000 --stdio-F 8000把采样频率提高到 8000Hz短期运行可以获得更多样本统计更准确。--stdio模式不会进入交互界面而是把当前采样结果直接输出到终端方便记录和 grep 过滤。5.2 采样频率怎么选不是越高越好采样频率直接影响统计精度和性能开销。太低则样本数不够热点函数可能被遗漏太高则中断过于频繁本身就占用大量 CPU甚至影响被分析程序的运行。我的经验值是这样选的定位粗粒度热点哪个模块、哪种锁4000Hz 足够这是 perf 的默认频率。需要更精确的函数排名可以提高到 8000~10000Hz但要同时观察 perf 自身 CPU 开销。用top看 perf 进程的 CPU 占用如果超过 5% 就说明频率太高了。分析短时间内的瞬态问题比如某次请求引起的 CPU 飙高用perf record -F 999配合离线分析更合适因为 real-time 模式很难捕捉到瞬间抖动。还有一个容易被忽略的点采样频率数值在部分 CPU 上只能取质数或特定值因为内核要避免采样周期和应用的主循环周期产生锁相。如果采样频率和应用的主循环频率成整数倍关系采样结果会出现明显的偏差。perf 工具内部会尽量避开这种锁相问题但你自己设置频率时选择 997、1999 这样的质数更安全。5.3 事件选择从 CPU 周期到缓存未命中perf top 默认采样的是cyclesCPU 周期这个事件测的是 CPU 执行指令耗费的时钟周期数适合大多数 CPU 占用率分析场景。但有些性能问题用 cycles 看不出来需要切换事件instructions采样已执行的指令数。如果 instructions 很高但 cycles 不高说明程序可能在频繁执行短小的循环。cache-misses采样 LLCLast Level Cache最后一级缓存未命中次数。如果 cache-misses 占比高说明程序局部性差大量时间在等待内存访问。这种现象在内存数据库、大规模数据处理场景很常见。branch-misses分支预测失败次数。如果这个高说明代码里存在难以预测的条件分支会导致 CPU 流水线冲刷。context-switches上下文切换次数。频繁切换意味着锁竞争、线程过多或系统调用密集。切换事件用快捷键E或者直接启动时指定-e eventsudo perf top -e cache-misses -p pid切换之后你会发现同一个程序在不同事件下的热点排名可能大相径庭。这是正常的因为不同事件衡量的是程序的不同方面。CPU 周期高不代表 cache miss 高cache miss 高也不代表 CPU 一定高。我见过一个数据库进程 cycles 正常但 cache-misses 居高不下导致实际执行效率很低。如果只看默认事件这个问题就被淹没了。6. 常见问题与排查技巧实录6.1 权限受限Operation not permitted 怎么处理CentOS 7.6 上执行 perf top 最常见的问题是权限不足。内核为了防止普通用户通过性能计数器获取敏感数据默认限制了 perf_event 的访问权限。错误信息一般是Error: You may not have permission to collect stats. Consider tweaking /proc/sys/kernel/perf_event_paranoid:解决方案有两个一是直接用 root 或 sudo 执行 perf top二是调整 perf_event_paranoid 参数sudo sysctl -w kernel.perf_event_paranoid-1这个参数的值含义是-1表示允许所有用户访问所有性能事件2表示只允许用户态事件拒绝内核态事件3表示只允许用户态事件且拒绝内核态且不允许 CPU 层面的事件。生产环境不建议长期设置成-1有安全风险。如果需要给开发账号开权限更稳妥的做法是在 sudoers 里给特定用户配置 perf 的免密 sudo而不是放开全局参数。6.2 符号全显示 unknown排查思路和解决办法如果你看到 perf top 界面里大量符号显示 unknown 或者十六进制地址说明符号解析失败。排查顺序是先确认当前用户是否是 root。普通用户看内核符号必然受限。确认内核调试符号包是否安装。执行ls /usr/lib/debug/lib/modules/$(uname -r)看看有没有内容。确认 perf 工具版本和内核版本是否一致。perf --version和uname -r对比。如果是容器环境符号解析会复杂很多因为 /proc/kallsyms 是宿主机的容器的符号表和宿主机不匹配。容器内直接用 perf top 通常效果不好建议在宿主机上用-p指定容器进程的 PID 来分析。补充一个细节JVM 跑到 perf top 里经常显示为[JIT]标签下的匿名符号这是因为 JIT 编译生成的机器码没有符号表。想看到 Java 方法的符号需要 JVM 开启-XX:PreserveFramePointer和-XX:PerfDataSaveToFile之类的参数并且配合 perf map 工具。这个话题展开会很长简单说就是perf 对 Java 的支持比 C/C 弱需要额外配合 perf-map-agent 之类的工具才能看到方法名。6.3 采样数据不稳定如何判断数据是否可信有同学会问perf top 的数据一直在跳到底该以哪个时刻为准这个问题其实没有标准答案但有一些经验可以分享。首先perf top 的实时数据本身就带有随机性每次刷新看到的都是上一秒的采样结果。如果某函数在多次刷新中稳定排在前几那大概率是真实热点如果排名忽高忽低可能是该函数本身波动大也可能只是采样噪声。其次判断数据可信度要看 Samples 总数。如果总样本数是几万那单个函数的样本数几百统计置信度就比较高如果总样本数只有几百单函数样本十几个那统计噪声就很大。这时候要么拉长观察时间要么提高采样频率。最后如果怀疑 perf top 结果受到中断采样偏差影响也就是采样点落在中断处理和内核路径上可以在启动时用--kallsyms指定 kallsyms 文件或者用perf report --stdio --sort symbol配合perf record做离线分析这种模式可以按需过滤事件统计结果更精细。6.4 在虚拟机里使用 perf top 需要注意什么现在的生产环境大量使用虚拟机虚拟机里跑 perf top 比物理机多一些坑。最典型的是虚拟机的 CPU 周期事件由宿主机调度采样结果可能包含宿主机调度引入的误差。如果你发现热点函数全部集中在kvm模块或者hypervisor相关符号上那基本可以判断是虚拟机 CPU 调度带来的噪声。这种情况下可以改用-e instructions或者其他软件事件来采样。软件事件由内核生成不依赖硬件 PMU在虚拟化环境下的精度相对好一些。另一个办法是把分析放到宿主机上做在宿主机上用-p指定虚拟机进程的 PID这样能看到包含虚拟化开销在内的完整调用链。另外容器环境下 perf top 也有一个比较常见的问题容器内的 /proc 是隔离的你看到的 pid 是容器命名空间里的 pid和宿主机上的 pid 不是一回事。如果你在宿主机上用-p指定容器内进程的 pid会直接报错找不到进程。需要用docker inspect或cat /proc/pid/status查到宿主机视角的 pid才能正确分析。跨命名空间分析容器性能是 perf 系列工具里比较复杂的一环等后续有时间可以单独写一篇讲容器和虚拟机场景的 perf 实践。7. 和 perf record、perf report 的配合使用7.1 为什么说 perf top 是快照perf record 是录像perf top 是实时采样并展示当前状态它适合快速定位正在发生的问题但有两个天生的局限一是数据不落盘历史数据无法回溯二是交互界面的展示形式限制了对复杂调用栈的分析深度。对应的perf record把采样数据记录到磁盘上的 perf.data 文件之后可以用perf report进行离线精细化分析。它的优势是可以反复查看、支持更灵活的过滤排序、还能配合脚本生成火焰图。一个适合用 perf record 的典型场景是问题只出现在凌晨某个时间点人不在现场那就提前写一个 crontab 任务定时采集一段时间的数据第二天再离线分析。常见的组合用法# 采集 sudo perf record -a -g -- sleep 60 # 离线分析 sudo perf report --stdio --sort symbol-a参数表示采集全系统的数据适合不确定问题进程到底是谁的场景。--sort symbol让报告按照函数符号聚合输出更清晰。7.2 现场问题排查的推荐流程结合这些工具我总结了一套适合 CentOS 7.6 排查 CPU 类问题的工作流第一步top 确认进程判断是哪个进程消耗 CPU。第二步perf top -p -g 快速观察热点函数和调用栈方向。第三步如果方向清晰直接针对性地调整配置或代码如果方向不清晰用 perf record -p -g -- sleep 30 记录现场数据。第四步用 perf report 或者火焰图脚本离线分析调用栈定位到具体代码路径。这套流程在多次线上问题排查中都很有效尤其是第四步生成的火焰图配合代码 review 基本能把问题锁定到一个函数甚至一行代码。要提醒的是perf record 采集期间会略微影响被采集程序的性能在极端高负载场景下可能加重问题。所以采样窗口不建议太长平时 30 秒已经足够。perf 还提供了一些高级子命令比如perf stat用来统计总的性能计数perf probe用来动态添加跟踪点perf trace类似 strace 但更强大。这些工具组合起来几乎覆盖了从系统级到应用级的全部性能分析场景但实际工作里 perf top 依然是出现频率最高的一个因为它最快、最直观、最容易给问题定性。8. 写在最后关于 perf top 的几个使用心得平时用 perf top 比较多积累了几个小经验。一个是采样时尽量排除干扰进程如果机器上有多业务混部建议-p指定目标进程否则其他进程的 CPU 事件会污染排序结果。另一个是不要只盯默认的 cycles 事件遇到诡异问题就切换 cache-misses 和 context-switches 看看经常会发现意外的线索。还有一点是关于学习和练习的在 CentOS 7.6 上练手最方便的办法是随便跑一个自己写的死循环程序然后用 perf top 观察热点。比如写一个while(1);的空转程序你就能在界面上看到它的函数名出现在最顶部。从这样一个简单的实验开始逐步加深对不同符号、不同事件、不同采样参数的理解比直接去啃内核文档或者复杂案例有效得多。我实际用下来的体会是perf top 虽然看起来只是一个实时性能监控命令但它背后连接着整个 perf 子系统、CPU 硬件计数器、内核符号解析、调用栈回溯这些深邃的主题。把这一条命令真正吃透对整个 Linux 性能分析体系的理解都会上一个台阶。下次再遇到 CPU 飙高的问题你也能像老手一样几分钟内就把方向指出来。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。