引言
在 Linux 系统运维和性能优化中,"看不见的问题最难排查"。当 CPU 负载飙升、内存泄漏隐蔽发生、磁盘 I/O 抖动不定时,我们需要一套系统化的工具链来"看见"内核的行为。本文将从经典调试工具 ftrace 出发,深入剖析 Linux 内核性能分析的完整技术栈,最终掌握现代 eBPF 可编程观测平台的实战技能。
一、Linux 性能剖析全景图
Linux 性能观测形成了从硬件到应用、从内核到用户空间的完整层次:
| 层次 | 工具类型 | 典型工具 |
|---|---|---|
| 硬件层 | 性能计数器 | perf stat, pmc, turbostat |
| 内核层 | 追踪/探针 | ftrace, kprobe, tracepoint, eBPF |
| 系统层 | 资源监控 | vmstat, iostat, sar, dstat |
| 进程层 | 进程跟踪 | strace, ltrace, gdb, lsof |
| 应用层 | APM 工具 | DTrace, SystemTap, bpftrace |
二、ftrace:内核函数追踪框架
ftrace 是 Linux 内核内置的函数追踪框架,无需额外安装,提供最轻量的内核行为观测手段。
2.1 ftrace 核心架构
ftrace 通过编译器插桩(-pg 选项)在每个函数入口插入跳转指令,实现运行时动态开关。主要组件包括:
- Function Tracer:函数调用追踪,可过滤、统计、图形化
- Function Graph Tracer:记录函数调用关系和执行时间
- Tracepoint:静态探针点,覆盖调度、网络、文件系统等
- kprobe/kretprobe:动态探针,任意内核函数入口/返回插桩
- uprobe/uretprobe:用户空间函数动态插桩
2.2 实战:追踪调度延迟
# 挂载 debugfs
mount -t debugfs none /sys/kernel/debug
# 切换到 function tracer
echo function > /sys/kernel/debug/tracing/current_tracer
# 设置过滤:只追踪 schedule 相关函数
echo 'schedule* schedule' > /sys/kernel/debug/tracing/set_ftrace_filter
# 开启追踪
echo 1 > /sys/kernel/debug/tracing/tracing_on
# 查看结果
cat /sys/kernel/debug/tracing/trace
# 关闭追踪
echo 0 > /sys/kernel/debug/tracing/tracing_on
2.3 trace-cmd:ftrace 封装利器
trace-cmd 提供了更友好的命令行接口,支持报告生成和网络传输:
# 记录执行 ls 期间的所有函数调用
trace-cmd record -p function -F ls
# 生成报告
trace-cmd report
# 指定 CPU 过滤
trace-cmd record -p function -C 0 -F ./my_app
三、perf:性能分析瑞士军刀
perf 是 Linux 主要的性能分析工具,集成性能计数、采样、追踪、剖析于一体。
3.1 CPU 性能计数器
perf stat -a sleep 10 # 系统级 CPU 事件统计
perf stat -p $(pidof nginx) # 进程级统计
perf stat -e cycles,instructions,cache-misses ./app
3.2 采样剖析(Profiling)
# CPU 火焰图采样
perf record -F 99 -a -g -- sleep 60
perf script > out.perf
# 生成可视化火焰图(配合 FlameGraph 工具)
./stackcollapse-perf.pl out.perf | ./flamegraph.pl > flame.svg
# 针对特定进程
perf record -F 99 -p $PID -g -- sleep 30
3.3 追踪与探测
# 追踪系统调用
perf trace -p $PID
perf trace -e 'syscalls:sys_enter_openat' -a
# 使用 kprobe 追踪内核函数
perf probe --add 'do_sys_open'
perf record -e probe:do_sys_open -aR sleep 10
四、eBPF:可编程内核观测平台
eBPF(Extended Berkeley Packet Filter)是近十年 Linux 最重要的技术突破之一,允许在内核中安全运行沙箱化程序,无需修改内核源码或加载模块。
4.1 eBPF 架构原理
eBPF 程序的生命周期:
- 编写:C 子集(或 Rust、Go)编写 eBPF 程序
- 编译:LLVM/Clang 编译为 eBPF 字节码
- 加载:通过 bpf() 系统调用加载入内核
- 验证:内核验证器检查安全性(无循环、有限栈、终止性)
- JIT:即时编译为 Native 机器码执行
- 挂载:附加到 hook 点(kprobe、tracepoint、XDP、cgroup 等)
- 通信:通过 BPF Maps 与用户空间交换数据
4.2 BPF Maps:数据交换机制
BPF Maps 是 eBPF 程序与用户空间通信的核心数据结构:
- Hash Map:键值对统计(如连接计数、延迟直方图)
- Array Map:固定大小数组,配置传递
- Ring Buffer:高效流式事件输出(替代 perf buffer)
- LRU Hash:自动淘汰的 LRU 缓存
- Per-CPU Maps:避免 CPU 间同步开销
- Queue/Stack:FIFO/LIFO 数据结构
4.3 BCC(BPF Compiler Collection)实战
BCC 提供了 Python 封装,快速编写 eBPF 工具:
#!/usr/bin/env python3
from bcc import BPF
# eBPF C 程序
prog = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
BPF_HISTOGRAM(dist, u64);
int do_trace(struct pt_regs *ctx) {
u64 ts = bpf_ktime_get_ns();
u64 delta;
// 过滤系统调用
if (PT_REGS_RC(ctx) < 0)
return 0;
delta = ts - PT_REGS_RC(ctx);
dist.increment(bpf_log2l(delta / 1000)); // us 级直方图
return 0;
}
"""
b = BPF(text=prog)
b.attach_kprobe(event="do_sys_openat2", fn_name="do_trace")
print("Tracing... Ctrl-C to end.")
try:
sleep(999999)
except KeyboardInterrupt:
pass
b["dist"].print_log2_hist("usecs")
4.4 bpftrace:一行命令的 eBPF 追踪
bpftrace 提供类 awk 语法,无需写 C 代码即可进行 eBPF 追踪:
# 统计每个进程 open() 系统调用次数
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
# 追踪 TCP 连接延迟
bpftrace -e 'kprobe:tcp_connect { @start[tick] = nsecs; }
kretprobe:tcp_connect /@start[tick]/ {
@us = hist((nsecs - @start[tick]) / 1000);
delete(@start[tick]);
}'
# 监控磁盘 I/O 大小分布
bpftrace -e 'tracepoint:block:block_rq_issue { @bytes = hist(args->bytes); }'
# 统计页面错误
bpftrace -e 'software:faults:1 { @[comm] = count(); }'
五、典型场景排查流程
5.1 CPU 飙高排查
# 1. 找出吃 CPU 的进程
top -H -p $PID
# 2. 采样生成火焰图
perf record -F 99 -p $PID -g -- sleep 30
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > cpu_flame.svg
# 3. 结合 off-CPU 分析(等待分析)
perf record -e sched:sched_switch -p $PID -g -- sleep 30
# 4. 使用 eBPF 追踪热点函数
funclatency -m 1 do_page_fault
5.2 内存泄漏定位
# 1. 查看整体内存趋势
sar -r 1 30
# 2. 追踪分配/释放不平衡
bpftrace -e 'kprobe:kmalloc { @[stack(5), comm] = count(); }'
# 3. 使用 BCC 的 memleak 工具
memleak -p $PID -o 600 # 追踪 10 分钟
# 4. slab 分配追踪
slabratetop # 类似 top,显示 slab 分配速率
5.3 I/O 抖动分析
# 1. 查看 I/O 统计
iostat -x 1
# 2. 追踪块设备延迟
biosnoop # BCC 工具
# 3. 分析 I/O 模式(顺序/随机)
biotop # 类似 top 的 I/O 视图
# 4. 延迟分布直方图
bpftrace -e 'kprobe:blk_account_io_done {
@us = hist((nsecs - @start[args->bio]) / 1000);
}'
5.4 网络延迟根因定位
# 1. TCP 重连统计
bpftrace -e 'kprobe:tcp_retransmit_skb { @[comm, args->sk->__sk_common.skc_daddr] = count(); }'
# 2. 连接建立延迟
tcplife # BCC 工具,显示每个连接的寿命
# 3. DNS 延迟
bpftrace -e 'kprobe:{udp_sendmsg, tcp_connect} { @ts[tick] = nsecs; }
kretprobe:{udp_recvmsg, tcp_recvmsg} /@ts[tick]/ {
@ms = hist((nsecs - @ts[tick]) / 1000000);
}'
六、eBPF 生态工具速查
| 工具 | 功能 | 安装命令 |
|---|---|---|
| execsnoop | 追踪 exec() 调用 | apt install bpfcc-tools |
| opensnoop | 追踪 open() 调用 | apt install bpfcc-tools |
| biosnoop | 块设备 I/O 追踪 | apt install bpfcc-tools |
| tcpconnect | 追踪 TCP 主动连接 | apt install bpfcc-tools |
| tcpaccept | 追踪 TCP 被动连接 | apt install bpfcc-tools |
| tcpretrans | 追踪 TCP 重传 | apt install bpfcc-tools |
| runqlat | CPU 运行队列延迟直方图 | apt install bpfcc-tools |
| runqlen | CPU 运行队列平均长度 | apt install bpfcc-tools |
| hardirqs / softirqs | 中断耗时统计 | apt install bpfcc-tools |
| biolatency | 块设备延迟直方图 | apt install bpfcc-tools |
| cachestat | 页面缓存命中率 | apt install bpfcc-tools |
| profile | CPU 采样(替代 perf record) | apt install bpfcc-tools |
七、内核追踪的底层原理对比
| 工具 | 机制 | 开销 | 灵活性 | 易用性 |
|---|---|---|---|---|
| ftrace | 静态插桩 + 动态 filter | 极低 | 中 | 中 |
| kprobe | 断点 + 单步执行 | 中 | 高 | 中 |
| tracepoint | 静态探针点 | 极低 | 低 | 高 |
| perf | PMU 计数器采样 | 低 | 中 | 高 |
| eBPF | 内核沙箱执行 | 极低 | 极高 | 高 |
八、生产环境最佳实践
- 分层排查:先用 perf stat 宏观定位,再用 eBPF 微观追踪根因
- 采样率控制:高吞吐场景(如 100Gbps 网络)使用采样而非全量追踪
- 用户空间聚合:eBPF 程序"轻内核、重用户",聚合逻辑在用户空间完成
- 版本兼容:生产环境优先使用 Tracepoint 或 CO-RE eBPF(一次编译处处运行)
- 权限最小化:使用 CAP_BPF、CAP_PERFMON 替代 root
- 监控集成:结合 Prometheus + Grafana 构建可观测平台(如 Pixie、Parca)
九、常用 BPF Helper 函数速查
bpf_probe_read*() // 安全读取内核内存
bpf_ktime_get_ns() // 获取纳秒时间戳
bpf_get_current_pid_tgid() // 获取 PID/TGID
bpf_get_current_comm() // 获取进程名
bpf_map_lookup_elem() // 查找 BPF Map
bpf_map_update_elem() // 更新 BPF Map
bpf_printk() // 调试输出(打印到 trace_pipe)
bpf_perf_event_output() // 向 perf ring buffer 输出事件
bpf_ringbuf_output() // 向 ring buffer 输出事件(推荐)
总结
Linux 性能剖析已形成从 ftrace 到 eBPF 的完整工具链:ftrace适合快速函数追踪,perf适合采样剖析和硬件事件,eBPF是终极的可编程观测平台。掌握这三层技术栈,配合火焰图、直方图可视化方法,能够系统化定位 CPU、内存、I/O、网络等维度的性能瓶颈。建议从 bpftrace 入手,遇到复杂场景再深入 BCC 或 libbpf 编写原生 eBPF 程序,逐步构建"看得见"的系统可观测能力。

发表评论 取消回复