CPU 被打满却说不清热点、延迟尖刺只能「重启试一下」、上线后回归全靠猜------缺的不是参数列表,而是 可复现的观测路径 :从 tracepoint/kprobe 到采样火焰图,再到改参与回归对比。本文把 ftrace、perf_event、eBPF/bpftrace、常见调优开关与排障顺序合成一篇闭环。
源码锚点
| 路径 | 作用 |
|---|---|
kernel/trace/ftrace.c |
ftrace 核心、function tracer |
kernel/trace/trace.c |
tracefs 环形缓冲与输出 |
include/linux/ftrace.h |
ftrace API |
kernel/events/core.c |
perf_event 子系统 |
kernel/kprobes.c |
kprobe 动态插桩 |
kernel/bpf/ |
eBPF 验证器、地图、运行时 |
kernel/trace/bpf_trace.c |
BPF 与 tracing 衔接 |
fs/tracefs/ / debugfs |
用户态控制面(挂载点因发行版而异) |
tools/perf/ |
用户态 perf 工具 |
Documentation/trace/、Documentation/bpf/ |
官方追踪与 BPF 文档 |
ftrace 控制面(示意):
bash
# 通常需要 root;路径可能是 /sys/kernel/tracing
mount | grep tracefs
cd /sys/kernel/tracing
echo function > current_tracer
echo do_sys_openat2 > set_ftrace_filter # 示例符号以本机为准
echo 1 > tracing_on
cat trace | head
echo 0 > tracing_on
perf 采样骨架:
bash
perf record -F 99 -g -- ./app
perf report
# 或系统范围
perf top
perf record -a -g -- sleep 10
调用链
观测数据从内核到用户态
#mermaid-svg-VUehsEsGWDM6kxoH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VUehsEsGWDM6kxoH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VUehsEsGWDM6kxoH .error-icon{fill:#552222;}#mermaid-svg-VUehsEsGWDM6kxoH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VUehsEsGWDM6kxoH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VUehsEsGWDM6kxoH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VUehsEsGWDM6kxoH .marker.cross{stroke:#333333;}#mermaid-svg-VUehsEsGWDM6kxoH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VUehsEsGWDM6kxoH p{margin:0;}#mermaid-svg-VUehsEsGWDM6kxoH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VUehsEsGWDM6kxoH .cluster-label text{fill:#333;}#mermaid-svg-VUehsEsGWDM6kxoH .cluster-label span{color:#333;}#mermaid-svg-VUehsEsGWDM6kxoH .cluster-label span p{background-color:transparent;}#mermaid-svg-VUehsEsGWDM6kxoH .label text,#mermaid-svg-VUehsEsGWDM6kxoH span{fill:#333;color:#333;}#mermaid-svg-VUehsEsGWDM6kxoH .node rect,#mermaid-svg-VUehsEsGWDM6kxoH .node circle,#mermaid-svg-VUehsEsGWDM6kxoH .node ellipse,#mermaid-svg-VUehsEsGWDM6kxoH .node polygon,#mermaid-svg-VUehsEsGWDM6kxoH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VUehsEsGWDM6kxoH .rough-node .label text,#mermaid-svg-VUehsEsGWDM6kxoH .node .label text,#mermaid-svg-VUehsEsGWDM6kxoH .image-shape .label,#mermaid-svg-VUehsEsGWDM6kxoH .icon-shape .label{text-anchor:middle;}#mermaid-svg-VUehsEsGWDM6kxoH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VUehsEsGWDM6kxoH .rough-node .label,#mermaid-svg-VUehsEsGWDM6kxoH .node .label,#mermaid-svg-VUehsEsGWDM6kxoH .image-shape .label,#mermaid-svg-VUehsEsGWDM6kxoH .icon-shape .label{text-align:center;}#mermaid-svg-VUehsEsGWDM6kxoH .node.clickable{cursor:pointer;}#mermaid-svg-VUehsEsGWDM6kxoH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VUehsEsGWDM6kxoH .arrowheadPath{fill:#333333;}#mermaid-svg-VUehsEsGWDM6kxoH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VUehsEsGWDM6kxoH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VUehsEsGWDM6kxoH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VUehsEsGWDM6kxoH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VUehsEsGWDM6kxoH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VUehsEsGWDM6kxoH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VUehsEsGWDM6kxoH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VUehsEsGWDM6kxoH .cluster text{fill:#333;}#mermaid-svg-VUehsEsGWDM6kxoH .cluster span{color:#333;}#mermaid-svg-VUehsEsGWDM6kxoH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VUehsEsGWDM6kxoH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VUehsEsGWDM6kxoH rect.text{fill:none;stroke-width:0;}#mermaid-svg-VUehsEsGWDM6kxoH .icon-shape,#mermaid-svg-VUehsEsGWDM6kxoH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VUehsEsGWDM6kxoH .icon-shape p,#mermaid-svg-VUehsEsGWDM6kxoH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VUehsEsGWDM6kxoH .icon-shape .label rect,#mermaid-svg-VUehsEsGWDM6kxoH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VUehsEsGWDM6kxoH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VUehsEsGWDM6kxoH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VUehsEsGWDM6kxoH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 静态
动态函数
PMU 采样
可编程
感兴趣事件
观测手段
tracepoint
kprobe / ftrace filter
perf_event
eBPF 程序
ring buffer / perf mmap
BPF map / ringbuf
用户态工具读取
报告 / 火焰图 / 指标
调优闭环分层
#mermaid-svg-hqHuOnUWouyZb6O2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hqHuOnUWouyZb6O2 .error-icon{fill:#552222;}#mermaid-svg-hqHuOnUWouyZb6O2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hqHuOnUWouyZb6O2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hqHuOnUWouyZb6O2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hqHuOnUWouyZb6O2 .marker.cross{stroke:#333333;}#mermaid-svg-hqHuOnUWouyZb6O2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hqHuOnUWouyZb6O2 p{margin:0;}#mermaid-svg-hqHuOnUWouyZb6O2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster-label text{fill:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster-label span{color:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster-label span p{background-color:transparent;}#mermaid-svg-hqHuOnUWouyZb6O2 .label text,#mermaid-svg-hqHuOnUWouyZb6O2 span{fill:#333;color:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 .node rect,#mermaid-svg-hqHuOnUWouyZb6O2 .node circle,#mermaid-svg-hqHuOnUWouyZb6O2 .node ellipse,#mermaid-svg-hqHuOnUWouyZb6O2 .node polygon,#mermaid-svg-hqHuOnUWouyZb6O2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hqHuOnUWouyZb6O2 .rough-node .label text,#mermaid-svg-hqHuOnUWouyZb6O2 .node .label text,#mermaid-svg-hqHuOnUWouyZb6O2 .image-shape .label,#mermaid-svg-hqHuOnUWouyZb6O2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-hqHuOnUWouyZb6O2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hqHuOnUWouyZb6O2 .rough-node .label,#mermaid-svg-hqHuOnUWouyZb6O2 .node .label,#mermaid-svg-hqHuOnUWouyZb6O2 .image-shape .label,#mermaid-svg-hqHuOnUWouyZb6O2 .icon-shape .label{text-align:center;}#mermaid-svg-hqHuOnUWouyZb6O2 .node.clickable{cursor:pointer;}#mermaid-svg-hqHuOnUWouyZb6O2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hqHuOnUWouyZb6O2 .arrowheadPath{fill:#333333;}#mermaid-svg-hqHuOnUWouyZb6O2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hqHuOnUWouyZb6O2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hqHuOnUWouyZb6O2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hqHuOnUWouyZb6O2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hqHuOnUWouyZb6O2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hqHuOnUWouyZb6O2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster text{fill:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 .cluster span{color:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hqHuOnUWouyZb6O2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hqHuOnUWouyZb6O2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-hqHuOnUWouyZb6O2 .icon-shape,#mermaid-svg-hqHuOnUWouyZb6O2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hqHuOnUWouyZb6O2 .icon-shape p,#mermaid-svg-hqHuOnUWouyZb6O2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hqHuOnUWouyZb6O2 .icon-shape .label rect,#mermaid-svg-hqHuOnUWouyZb6O2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hqHuOnUWouyZb6O2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hqHuOnUWouyZb6O2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hqHuOnUWouyZb6O2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 验证
假设与改动
观测
定义问题
延迟? CPU? IO? 唤醒?
建立 baseline
perf / top / iostat
ftrace / trace-cmd
bpftrace / BCC
调度/内存/IO/驱动参数
代码热点修复
同负载复测
回滚开关
重点知识
1. 先定性,再选工具
| 症状 | 先看 | 深挖 |
|---|---|---|
| CPU 高 | perf top/record -g |
符号、锁、软中断 |
| 延迟尖刺 | cyclictest、调度/中断追踪 |
irqsoff/preemptirqsoff、唤醒链 |
| IO 等 | iostat、perf block 事件 |
电梯、队列深度、FS 回写 |
| 内存 | /proc/meminfo、psi |
reclaim、oom、cgroup 限制 |
没有 baseline(同负载下的延迟/CPU/带宽数字)就改 sysctl,属于盲调。
2. ftrace:低成本看清调用与关闭抢占
- function / function_graph:看谁调用谁、耗时轮廓。
- irqsoff / preemptoff:关中断/关抢占过久(驱动里常见)。
- sched / wakeup:任务为何睡、被谁唤醒。
bash
trace-cmd record -e sched:sched_switch -e sched:sched_wakeup sleep 5
trace-cmd report | head
注意:过滤不当时开销与 trace 体积会爆;生产先短时、窄 filter。
3. perf:PMU 采样与火焰图
perf_event 把硬件计数器/软件事件接到统一接口;perf record 采样调用栈,适合「CPU 花在哪」。
bash
# 权限相关
sysctl kernel.perf_event_paranoid
# 常见:cache-misses、cycles、page-faults
perf stat -e cycles,cache-misses,page-faults -- ./app
解读要点:采样有偏差;内联/缺符号会「糊」;先保证 vmlinux/debuginfod 或至少 kallsyms 可读。
4. eBPF:验证后可编程观测
流程:编写(或 bpftrace 一行)→ verifier 保证安全 → 加载 → 挂到 kprobe/tracepoint/XDP 等 → map/ringbuf 汇总。
bash
# 示例:统计 syscall 次数(语法随 bpftrace 版本微调)
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
约束:
- 无 verifier 通过则无法加载------这是特性不是障碍;
- 生产控制 attach 范围与采样率,避免自扰动;
- 权限:通常需要
CAP_BPF/CAP_PERFMON或 root(视内核与发行版)。
5. kprobe 与开销意识
kprobe 可动态插到几乎任意内核函数,但:
- 热路径高频命中会显著扰动;
- 部分函数不可探测或随版本改名;
- 优先用稳定 tracepoint,kprobe 作补充。
6. 配置与安全
bash
# 追踪文件系统
mount -t tracefs tracefs /sys/kernel/tracing
# BPF 文件系统(部分工具需要)
mount -t bpf bpf /sys/fs/bpf
| 项 | 说明 |
|---|---|
kernel.perf_event_paranoid |
控制非特权 perf 能力 |
| debugfs/tracefs 权限 | 避免无关用户读内核地址信息 |
| 生产追踪 | 限时、限事件、有回滚 |
7. 从观测到改参:几类高频旋钮(先测后改)
| 领域 | 示例入口 | 说明 |
|---|---|---|
| 调度 | schedutil、CPU 亲和、nice/cgroup cpu |
先确认是否真 CPU 饱和 |
| 内存 | vm.swappiness、vfs_cache_pressure、cgroup 限制 |
配合 PSI/meminfo |
| 网络 | somaxconn、队列、NAPI/中断开销 |
先 ss/perf 再改 |
| 块 IO | 调度器、队列深度、ionice |
先分清读/写/刷新 |
| 驱动 | 线程化 IRQ、合并工作、避免关中断过久 | 用 irqsoff 验证 |
原则:一次只改一类变量 ,保留开关与回滚命令。容器/ cgroup 场景下,宿主机 sysctl 与容器限额要分开看。
8. 常见坑
| 坑 | 结果 | 处理 |
|---|---|---|
| 无符号表 | 火焰图全是地址 | 安装 debug 包 / 带 vmlinux |
| 同时开太多 tracer | 机器变慢、数据失真 | 单次一种目的 |
| 只调参不复测 | 「好像好了」 | 固定负载脚本对比 |
| 把平均当尾延迟 | P99 仍炸 | 看直方图/百分位 |
| 在生产长期开 function tracer | 吞吐断崖 | 限时、窄 filter、事后关闭 |
Checklist
- 调优前有书面 baseline(负载描述 + 关键指标)
- 能说明 ftrace / perf / eBPF 各自擅长的问题类型
- 会用
perf record -g+report定位用户/内核热点 - 会在 tracefs 上做一次窄过滤的 function 追踪并关闭
- 优先选用 tracepoint,明白 kprobe 的开销与版本风险
- 知道
perf_event_paranoid与 tracefs 挂载对工具的影响 - 每次改动可回滚,并用同一负载复测验证
小结
内核已经把观测做成可组合管线:事件源 → 缓冲/映射 → 用户态分析。调优的设计意图不是堆参数,而是用 ftrace/perf/eBPF 把假设证伪或证实。顺序固定为:定性 → 选工具 → 短时窄范围采集 → 改一处 → 对比 baseline。