015. 什么是skeleton
这是一个非常关键的概念性问题,而且你前面已经把 “非 skeleton 的 libbpf loader” 全部拆完了——
现在来讲 skeleton,刚好是“从原理到工程”的闭环。
我按 一句话定义 → 本质 → 它解决了什么问题 → 它到底帮你干了什么 → 什么时候该用 / 不该用 → 课堂总结 来讲。
一句话定义
**eBPF skeleton 是 libbpf 根据 eBPF ELF 文件自动生成的一份 C 代码,
****用来封装 libbpf 的 open / load / attach / destroy 过程,
**让用户态以“结构体 + 函数”的方式操作 eBPF 程序。
一句更直白的:
skeleton = “帮你把 libbpf loader 代码写好了”。
二、eBPF skeleton 的本质是什么?(非常重要)
❗ 它不是新机制
- ❌ 不是新的 BPF program
- ❌ 不是新的 attach 方式
- ❌ 不是内核特性
✅ 它的本质只有一个
代码生成(code generation)
你执行:
bpftool gen skeleton xxx.bpf.o > xxx.skel.h
得到的 .skel.h 本质是:
一堆“提前写好的 libbpf API 调用代码”
三、为什么需要 skeleton?(动机)
在没有 skeleton 之前,你要手写很多重复代码:
bpf_object__open_file()
bpf_object__load()
bpf_object__find_program_by_name()
bpf_program__attach_xxx()
bpf_map__fd()
bpf_object__close()
问题是:
- 代码冗长
- 容易写错
- API 易变
- 维护成本高
👉 skeleton 的目标:
把 80% 的“样板代码”自动生成。
四、skeleton 到底帮你干了哪些事?(核心)
假设你的 BPF 程序里有:
SEC("uprobe")
int uprobe_malloc(...) {}
SEC("uretprobe")
int uretprobe_malloc(...) {}
struct {
__uint(type, BPF_MAP_TYPE_HASH);
} allocs SEC(".maps");
skeleton 会自动生成:
1️⃣ 一个强类型的“总结构体”
struct malloc_bpf {
struct bpf_object *obj;
struct {
struct bpf_program *uprobe_malloc;
struct bpf_program *uretprobe_malloc;
} progs;
struct {
struct bpf_map *allocs;
} maps;
struct {
struct bpf_link *uprobe_malloc;
struct bpf_link *uretprobe_malloc;
} links;
};
👉 不需要再 find program / find map
2️⃣ 标准生命周期函数
struct malloc_bpf *malloc_bpf__open(void);
int malloc_bpf__load(struct malloc_bpf *skel);
int malloc_bpf__attach(struct malloc_bpf *skel);
void malloc_bpf__destroy(struct malloc_bpf *skel);
👉 完整覆盖:open → load → attach → destroy
3️⃣ 自动 attach(按 SEC)
skeleton__attach() 内部会:
- 根据 SEC 类型
- 自动选择 attach API
- 自动生成
bpf_link
五、用 skeleton 后,你的用户态代码会变成什么样?
不用 skeleton(你现在讲的)
obj = bpf_object__open_file(...);
bpf_object__load(obj);
prog = bpf_object__find_program_by_name(obj, "uprobe_malloc");
link = bpf_program__attach_uprobe_opts(...);
用 skeleton(工程推荐)
skel = malloc_bpf__open();
malloc_bpf__load(skel);
malloc_bpf__attach(skel);
👉 逻辑没变,只是“展开方式不同”
六、skeleton 的“能力边界”(非常重要)
skeleton 擅长的
| 场景 | 是否适合 |
|---|---|
| 单 binary attach | ✅ |
| 固定 hook | ✅ |
| demo / 工具 | ✅ |
| 教学入门 | ✅ |
| 简洁工程 | ✅ |
skeleton 不擅长的(你前面讲过的那些)
| 场景 | 原因 |
|---|---|
| 动态选择 attach 点 | attach 写死 |
| 多进程 / 多 binary | 结构不灵活 |
| ELF 符号扫描 | skeleton 不管 |
| tracing 框架 | 控制力不足 |
| bpftool 替代品 | ❌ |
👉 这正是你“先讲非 skeleton,再讲 skeleton”的价值所在
七、和你前面所有内容的“终极对应关系”
你前面讲的:
open → load → find program → attach → destroy → close
在 skeleton 里:
__open → __load → __attach → __destroy
| 原理步骤 | skeleton 是否存在 |
|---|---|
| open | ✅ |
| load | ✅ |
| find program | ❌(已生成) |
| attach | ✅ |
| destroy | ✅ |
| close | ✅(内部) |
八、课堂级总结(强烈推荐用这段)
**eBPF skeleton 并没有改变 eBPF 的工作原理,
****它只是把你迟早要写的 libbpf 用户态代码,
**通过代码生成的方式提前写好了。
或者更狠一点:
**理解 libbpf,才能理解 skeleton;
**不理解 libbpf,就只能“用 skeleton 碰运气”。
九、一句终极定义
eBPF skeleton = libbpf API 的“自动展开版本”。

浙公网安备 33010602011771号