说明
在 BPF CO-RE 框架下,vmlinux.h
文件包含了内核的数据结构定义,这些定义是通过解析内核 BTF(BPF Type Format)信息自动生成的。BTF 是一种新的内核数据类型格式,它为内核提供了一种描述数据类型的方法,这对于 BPF CO-RE 的运行至关重要。
在编写 eBPF 程序时,vmlinux.h
文件使得开发者可以在用户空间程序中直接使用内核的数据结构,而无需关心内核版本的差异。这是因为 BPF CO-RE 可以在编译时解析这些数据结构,并生成可以在任何内核版本上运行的 eBPF 字节码。
例如,假设你在 eBPF 程序中需要访问 task_struct
结构,你可以在你的 eBPF 程序中包含 vmlinux.h
,然后直接使用 struct task_struct
。
所以,vmlinux.h
是对于BPF CO-RE
编程必不可少的文件。
vmlinux
vmlinux
是未压缩的 Linux 内核映像。它是在 Linux 内核编译过程中生成的一个文件,包含了整个内核的代码(包括内核模式和用户模式的代码)。vmlinux
包含了内核的符号表,因此它经常被用于调试目的。
vmlinux.h
是一个由 BPF CO-RE(Compile Once, Run Everywhere)框架自动生成的头文件。它包含了内核的数据结构定义,这些定义是通过解析 vmlinux
中的 BTF(BPF Type Format)信息自动生成的。
BTF 是一种新的内核数据类型格式,它为内核提供了一种描述数据类型的方法。通过 BTF,BPF CO-RE 可以解析 vmlinux
中的数据结构定义,并将这些定义写入 vmlinux.h
文件。
因此,vmlinux
和 vmlinux.h
之间的关系是:vmlinux.h
是通过解析 vmlinux
中的 BTF 信息生成的。vmlinux
包含了内核的代码和数据结构定义,而 vmlinux.h
包含了这些数据结构定义的副本,可以在编写 eBPF 程序时使用。
在编写 eBPF 程序时,vmlinux.h
文件使得开发者可以在用户空间程序中直接使用内核的数据结构,而无需关心内核版本的差异。这是因为 BPF CO-RE 可以在编译时解析这些数据结构,并生成可以在任何内核版本上运行的 eBPF 字节码。具体的原理结构图如下所是:
生成vmlinux.h文件
下面的命令是在Ubuntu 20.04
上生成的,其他系统可能会有所不同。
安装bpftool
1 | sudo apt install linux-tools-common |
成功安装之后提示:1
2
3
4
5
6
7
8
9
10$bpftool --help
WARNING: bpftool not found for kernel 5.15.0-76
You may need to install the following packages for this specific kernel:
linux-tools-5.15.0-76-generic
linux-cloud-tools-5.15.0-76-generic
You may also want to install one of the following packages to keep up to date:
linux-tools-generic
linux-cloud-tools-generic
按照要求安装:1
sudo apt install linux-tools-5.15.0-76-generic
生成vmlinux.h文件
1 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h |
CORE
由于 vmlinux.h
文件是从本机安装的内核生成的,如果试在另一台运行不同内核版本的机器上运行它而不重新编译,程序可能会出现问题。这是因为在不同版本之间,Linux 源代码中的内部结构定义会发生变化。
然而,通过使用 libbpf,可以启用称为CO:RE
(Compile once, run everywhere
)的功能。在 libbpf 中定义了一些宏(例如 BPF_CORE_READ),它们将分析您在 vmlinux.h 中定义的类型中尝试访问的字段。如果您要访问的字段已经在运行内核使用的结构体定义中移动,这些宏/辅助函数将为您找到它。无论您是否使用从自己的内核生成的 vmlinux.h 文件编译 bpf 程序,然后在不同的内核上运行,都没有关系。
在vmlinux.h
的顶部还存在如下的宏定义:1
2
3
__attribute__((preserve_access_index))
是 Clang 编译器提供的一个属性,用于告诉编译器在生成代码时保留对结构体成员的访问索引。这个属性通常用于与 eBPF 相关的代码中,以确保结构体成员的访问索引在 eBPF 程序中保持一致。
1 |
这个特性也会在最后一行被关闭。
在 Linux 中定义的宏并没有在 DWARF/BTF 中定义,并且不会成为生成的 vmlinux.h 文件的一部分。
参考
https://yanhang.me/post/2021-vmlinux/
https://www.ebpf.top/post/intro_vmlinux_h/
https://www.grant.pizza/blog/vmlinux-header/