☰ 第 15 章 · BPF:可编程动态观测
COURSE SOURCES

参考来源与版权说明

仅作来源说明 · 不提供原始资料下载
查看全部来源说明
01
START HERE

这章解决什么问题

传统跟踪器常把每个事件送到用户空间再处理;事件一旦高频,数据复制与解析本身就可能不可接受。eBPF 提供内核内的受控执行环境,让工具在事件发生时过滤、关联、计算延时,并只把计数或直方图等摘要送出。

本章聚焦两个前端:BCC 提供大量完成度高、带参数和文档的专用工具;bpftrace 用接近 awk 的“探针 / 过滤器 / 动作”语言,适合快速回答一次性问题。目标不是背语法,而是学会以最小探针和最低事件率得到足够证据。

02
SECTION 02

五条核心结论

  1. BPF 的价值是内核内可编程聚合。 过滤、map、栈和延时计算减少了逐事件上送的成本。
  2. 先找 BCC 成品,再写 bpftrace。 常见的 CPU、内存、文件系统、磁盘和网络问题通常已有经过验证的工具。
  3. 稳定探针优先。 tracepoint 和 USDT 提供显式接口;kprobe/uprobe 更灵活,却依赖函数、二进制、ABI 和版本。
  4. map 是跨事件状态与统计核心。 用线程 ID 等键关联进入/返回,用 count、sum、hist、lhist 得到可解释摘要。
  5. “通过验证器”不等于“无开销”。 探针频率、栈采集、字符串读取、map 键数量和用户态打印仍会消耗资源。
03
SECTION 03

核心模型与关键指标

bpftrace 程序的最小结构是:

~~~text

probe /filter/ { actions }

事件到达

→ 过滤掉无关对象

→ 读取参数/栈/时间

→ 更新 BPF map

→ 周期性或结束时打印摘要

~~~

测量函数延时通常在入口记录 <code>@start[tid] = nsecs</code>,在返回探针用同一 tid 取回开始时间,计算差值后写入 <code>hist()</code>,最后删除键。使用 tid 而非单一全局值,是为了防止并发线程互相覆盖;返回探针还要过滤没有开始记录的旧调用。

重要数据质量指标包括:探针触发率、附加成功/失败数、丢事件、map Entries 和容量、直方图桶、采集时间、目标 PID/cgroup,以及 BPF 程序自身 CPU 开销。统计摘要必须保留单位;以微秒记录就让 map 名或标题明确写出 us。

BCC 工具通常由用户空间程序负责参数、符号和展示,由内核中的 BPF 程序处理高频事件;bpftrace 把同一模式压缩成短语言。两者最终都受内核能力、BTF/调试信息和权限限制。选择前端时应看问题复杂度与交付寿命:一次验证用短程序,长期运行且需要稳定参数、测试和异常处理时更适合成熟工具。

许多 count、sum、hist 采用每 CPU map 以减少并发竞争,结束时再汇总;直接对一个全局整数自增可能出现竞争误差。另一方面,按 PID、栈和路径建立复合键虽然信息丰富,却会提高 map 基数。准确性、维度和开销需要一起设计。

04
SECTION 04

诊断步骤

  1. 把问题写成事件与字段。 例如“哪个进程的块 I/O 延时长”,需要开始/完成事件、设备、进程与时间。
  2. 搜索现成工具。 优先试 biolatency、runqlat、offcputime、tcplife 等 BCC 工具及其帮助/示例。
  3. 确认本机探针。 用 bpftrace 列表与详细列表核对 tracepoint、函数和参数,避免照搬旧内核名称。
  4. 先测事件率。 用 1 秒计数判断是否能安全逐事件处理;频率高时只做内核内聚合,避免 printf 和完整栈。
  5. 严格限定范围。 加 PID、cgroup、进程名、错误码或阈值过滤,并设置短时运行。
  6. 验证和复查。 与 perf、sar、应用指标交叉验证,检查未配对键与 map 增长,保存内核/BCC/bpftrace 版本。
05
SECTION 05

常用工具与命令

这些命令以观测为目的,但加载 BPF 程序通常需要 root 或相应 capabilities。发行版可能给 BCC 工具加 <code>.py</code> 后缀;函数名和 bpftrace 简写也会随版本变化。

~~~bash

# 只读列出探针及 tracepoint 参数

bpftrace -l 'tracepoint:block:*'

bpftrace -lv 'tracepoint:syscalls:sys_enter_read'

# 先用 1 秒计数估算 vfs_read 探针频率

bpftrace -e 'kprobe:vfs_read { @calls = count(); } interval:s:1 { exit(); }'

# 把 read 请求大小汇总成 2 的幂级直方图

bpftrace -e 'tracepoint:syscalls:sys_enter_read { @read_bytes = hist(args->count); }'

# 关联 vfs_read 进入/返回,输出微秒延时分布

bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; }

kretprobe:vfs_read /@start[tid]/ {

@latency_us = hist((nsecs - @start[tid]) / 1000);

delete(@start[tid]);

}'

# 优先尝试已有 BCC 工具;具体名称以软件包为准

biolatency -m

runqlat

~~~

先在测试或单实例上验证。不要把 <code>system()</code>、<code>signal()</code> 或覆盖返回值等能力当作普通观测手段;它们会改变外部状态或程序行为,本课程不提供这类操作示例。

06
SECTION 06

如何解读结果

hist 使用左闭右开桶,例如 [8, 16) 表示大于等于 8、小于 16。双峰分布通常提示两类路径、缓存命中/未命中或不同请求类型,应继续按进程、操作标志、文件系统或栈细分,而不是只算一个平均值。

BCC 工具的默认输出往往已选择了合适事件与聚合方式,但仍要看帮助中单位、范围和过滤语义。bpftrace map 键越细,解释力越强、基数和内存也越大;按 PID + 栈 + 文件名同时分组可能迅速膨胀。没有输出可能表示事件没发生,也可能是探针不存在、权限不足或过滤器错误。

入口/返回关联还要考虑未完成调用:跟踪开始时可能先看到返回,结束时也可能留下尚未返回的开始键。过滤 <code>@start[tid]</code> 能处理前一种情况,删除键并限制运行时长能控制后一种情况。跨线程完成的异步操作则不能用 tid 简单配对,必须寻找请求指针或事件自带标识。

07
SECTION 07

常见误判

  • 对所有网络包、调度或分配事件逐条 printf,造成高额开销和丢事件。
  • 直接使用旧版教程的 kprobe 函数名、参数寄存器或结构偏移。
  • 用单一全局开始时间关联并发调用,得到负值或混配延时。
  • 返回路径缺失时不删除/限制 map,长期运行导致键持续增长。
  • 只看平均延时,错过长尾、双峰和按业务对象分组后的差异。
  • 认为 BPF 安全验证器会自动保证性能安全和业务无扰动。
08
SECTION 08

五分钟自测

  1. 为什么 BPF 在高频事件上比“全部上送用户空间”更实用?
  2. 常见问题已有成熟工具时,应先选 BCC 还是手写 bpftrace?
  3. 入口/返回延时关联为什么常以 tid 为键?
  4. tracepoint 相比 kprobe 的主要工程优势是什么?
  5. bpftrace 没有输出时要核查哪三类原因?

检查要点:内核内过滤/聚合;BCC;避免并发覆盖;接口更稳定;事件未发生、探针/权限不可用、过滤条件不匹配。

09
SECTION 09

本章小结

BPF 让“提出问题 → 选择事件 → 内核内聚合 → 输出证据”变得可编程。可靠实践是先用成品工具,确认事件频率与字段,再写短小 bpftrace;始终限制范围、时长和 map 基数,并把版本与开销纳入结果。

10
SECTION 10

来源定位

主题PDF 页码
BPF 价值与 BCC/bpftrace 对比797-800
BCC 工具范围与使用方式800-807
bpftrace 工具与单行命令定位807-812
程序结构、探针、过滤器与变量812-820
探针类型、函数、map 与直方图820-827
文档与版本演进827-828
📝
QUICK CHECK

章节测验

5 道单选题。答错有解析,答对看来源页码。

单选题 · 第 1/5
已答 0/5

面对常见的磁盘延时问题,通常最合适的起点是什么?

⚡ 用本章题目开始计时冲刺