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 的“自动展开版本”。

posted @ 2026-06-02 00:33  BlackSnow  阅读(22)  评论(0)    收藏  举报