008.什么是BTF
下面给你一个从“直觉 → 机制 → 实战价值”的完整解释。
读完你不仅知道 BTF 是什么,还会知道为什么 eBPF / CO-RE 离不开它。
一句话定义(先记住这个)
**BTF(BPF Type Format)是 Linux 内核里的“类型元数据”,
****用来描述结构体、字段、函数原型等信息,
**让 eBPF 程序在不同内核版本上仍然能正确访问数据。
一、先用一个类比理解(非常重要)
没有 BTF 的世界
- eBPF 程序 写死结构体偏移
- 内核一升级:
- 字段位置变了
- 程序就读错数据 / verifier 拒绝
👉 “一次编译,只能在一个内核跑”
有 BTF 的世界
- 内核自带一份 “真实的类型说明书”
- eBPF 程序说的是:
“我要
task_struct.pid”
- libbpf 在加载时查 BTF:
“当前内核里
pid在 offset = 123”
👉 “一次编译,到处运行(CO-RE)”
二、BTF 到底包含什么信息?
BTF 是二进制格式,里面记录了:
1️⃣ 结构体定义
struct task_struct {
pid_t pid;
char comm[16];
...
};
2️⃣ 字段名 + 类型 + 偏移
task_struct.pid → offset 123, type = int
3️⃣ 函数原型(用于 fentry/fexit)
do_exit(long code)
4️⃣ typedef / enum / 指针 / 数组
👉 几乎是 C 类型系统的一个子集
三、BTF 在系统里的“真实形态”
| 位置 | 含义 |
|---|---|
/sys/kernel/btf/vmlinux |
运行内核的 BTF(真相源) |
.BTF ELF section |
编译产物中的 BTF |
vmlinux.h |
从 BTF 导出的 C 头文件(给人看) |
⚠️ 再强调一次:
BTF ≠ vmlinux.h
vmlinux.h 是 BTF 的“翻译件”
四、BTF 在 eBPF 里的三大核心作用
1️⃣ 支撑 CO-RE(最重要)
pid = BPF_CORE_READ(task, pid);
背后发生了什么:
- 编译期:字段名来自
vmlinux.h - 加载期:libbpf 查 运行内核的 BTF
- verifier 校验偏移合法
- 生成正确的内存访问指令
👉 这一步没有 BTF,CO-RE 不存在
2️⃣ 让 verifier “看懂你的程序”
- verifier 需要知道:
- 指针指向什么类型
- 是否越界
- BTF 提供 类型上下文
👉 没有 BTF,复杂 eBPF 程序几乎无法通过 verifier
3️⃣ 支持 fentry / fexit(函数级 tracing)
SEC("fentry/do_exit")
int BPF_PROG(do_exit, long code)
- 参数列表来自 内核函数的 BTF
- 不用 pt_regs
- 类型安全、性能更高
五、没有 BTF 会怎样?
你会遇到这些现实问题:
| 场景 | 结果 |
|---|---|
读 task_struct |
❌ 无法跨内核 |
| CO-RE | ❌ 不存在 |
| fentry/fexit | ❌ 无法 attach |
| verifier | ❌ 更容易拒绝 |
| 内核升级 | ❌ 全部重编译 |
👉 这就是“老 eBPF”为什么这么痛苦
六、BTF 是怎么来的?
1️⃣ 编译内核时生成
内核配置必须开启:
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_BTF=y
2️⃣ 内核启动后暴露给用户态
/sys/kernel/btf/vmlinux
3️⃣ 用户态工具使用
- libbpf
- bpftool
- verifier
七、你现在最容易混淆的 3 个点(帮你一次性理顺)
❌ 误解 1:BTF = 调试信息(DWARF)
- ❌ 不完全
- ✅ BTF 是 为 eBPF 定制的轻量类型系统
❌ 误解 2:有 vmlinux.h 就够了
- ❌ 错
- ✅ 运行内核必须真的有 BTF
❌ 误解 3:uprobe 也一定要 BTF
- ❌ 错
- ✅ 只做用户态 uprobe 可以完全不用 BTF
八、用一句“工程级总结”收尾(可直接写课件)
**BTF 是 Linux 内核给 eBPF 准备的一套“自描述类型系统”,
****它让程序不再依赖固定内存布局,
**是 CO-RE、fentry、现代 eBPF 的根基。

浙公网安备 33010602011771号