如何使用 eBPF 工具 bpftrace 实时追踪 Linux 内核慢调用?

文章导读
bpftrace 适合在内核版本较新且需要快速定位内核态耗时场景下使用,它不需要编译内核模块,适合临时排查。
📋 目录
  1. 环境检查与 BTF 确认
  2. 安全实操示例
  3. 输出结果解读
  4. 验证与风险控制
  5. 常见坑
A A

bpftrace 适合在内核版本较新且需要快速定位内核态耗时场景下使用,它不需要编译内核模块,适合临时排查。

先说结论:对于临时性的内核慢调用排查,bpftrace 是比 SystemTap 或自定义内核模块更轻量、更安全的选择,但必须避免使用通配符探针。

  • 先定位:确认系统内核支持 eBPF 且已安装 bpftrace 工具,建议内核版本 4.9+ 并开启 BTF。
  • 先做:使用具体函数名(如 do_sys_open)而非通配符,避免全量抓取导致系统宕机。
  • 再验证:观察工具运行期间的系统负载,确保追踪本身未造成明显性能抖动。

环境检查与 BTF 确认

在运行任何脚本前,需确认内核支持特定的探针点。盲目运行可能导致命令无效或报错。

1. 检查内核版本:运行 uname -r,建议 4.9 以上版本,4.19+ 支持更佳。

2. 确认探针可用性:使用 -l 参数列出可用探针,避免脚本执行失败。

sudo bpftrace -l 'kprobe:do_sys_open'

如果输出为空,说明该内核版本无此符号,需尝试 sys_open 或其他相关函数。

如何使用 eBPF 工具 bpftrace 实时追踪 Linux 内核慢调用?

3. 检查 BTF 支持:BTF 能提供更丰富的类型信息。检查 /sys/kernel/btf/vmlinux 是否存在。

安全实操示例

严禁在生产环境使用 kprobe:* 通配符,这会挂钩所有内核函数,导致 CPU 满载甚至宕机。以下是针对具体场景的安全脚本。

场景 1:统计文件打开延迟分布

该脚本追踪 do_sys_open 函数的入口和出口,计算耗时并生成直方图。

sudo bpftrace -e 'kprobe:do_sys_open { @start[tid] = nsecs; } kretprobe:do_sys_open /@start[tid]/ { @lat = hist(nsecs - @start[tid]); delete(@start[tid]); }'

场景 2:捕捉超过 10ms 的慢调用

仅打印耗时超过阈值的调用信息,减少输出开销。

如何使用 eBPF 工具 bpftrace 实时追踪 Linux 内核慢调用?
sudo bpftrace -e 'kprobe:do_sys_open { @start[tid] = nsecs; } kretprobe:do_sys_open /@start[tid]/ { $delta = nsecs - @start[tid]; if ($delta > 10000000) { printf("Slow open: %s by pid %d (%d us)\n", comm, pid, $delta / 1000); } delete(@start[tid]); }'

输出结果解读

运行直方图脚本后,终端会输出类似以下内容:

@lat:
(..., 0)             1 |
(0, 1)              15 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(1, 2)              5  |@@@@@@@@@@@@@@@@@@@@
(2, 4)              2  |@@@@@@@@
(4, 8)              1  |@@@@
(8, 16)             0  |
(16, 32)            0  |
(32, 64)            0  |
(64, 128)           0  |
(128, 256)          0  |
(256, 512)          0  |
(512, 1024)         0  |
(1024, 2048)        0  |
(2048, 4096)        0  |
(4096, 8192)        0  |
(8192, 16384)       0  |
(16384, 32768)      0  |
(32768, 65536)      0  |
(65536, 131072)     0  |
(131072, 262144)    0  |
(262144, 524288)    0  |
(524288, 1048576)   0  |
(1048576, 2097152)  0  |
(2097152, 4194304)  0  |
(4194304, 8388608)  0  |
(8388608, 16777216) 1  |@@@@
(16777216, 33554432) 0  |

单位默认为纳秒(nsecs)。例如 (8388608, 16777216) 表示耗时在 8ms 到 16ms 之间的调用有 1 次。若发现高延迟区间有数据,需结合业务日志进一步排查。

验证与风险控制

1. 系统负载检查:在另一个终端运行 tophtop,观察 bpftrace 进程本身的 CPU 占用。正常情况应低于 5%,若过高说明探针频率过高或函数过于高频。

2. 业务影响确认:对比开启追踪前后的业务响应时间。如果延迟明显增加,应立即停止追踪。

3. 安全停止:使用 Ctrl+C 终止命令。bpftrace 会在退出时自动清理加载的 eBPF 程序,无需手动卸载模块。

常见坑

  • 内核版本限制:旧版内核对 eBPF 功能支持有限,某些探针类型可能不可用,运行前务必确认内核文档。
  • 探针稳定性:虽然 eBPF 有验证器,但挂钩到某些高频或不稳定的内核函数仍可能导致系统卡顿,生产环境慎用通配符探针。
  • 锁竞争问题:在高并发场景下,频繁写入 bpftrace 的 map 可能引发锁竞争,建议先用低频率采样测试。
  • 安全策略拦截:部分 hardened 内核或安全模块(如 SELinux)可能阻止 eBPF 程序加载,需检查 dmesg 日志确认是否有拒绝记录。