1 1.1 前言
eBPF 是一项令人兴奋的强大技术,其允许开发者在 Linux 内核的核心处添加自定义代码功能,并且我们还可以通过编写简单的 C 或 Go 程序与加载到内核中的 eBPF 程序交互,用于加载或读取数据。运行在内核中的 BPF 程序可以检查所附加进程(或内核模块)的内存数据。为此,eBPF 程序需要确切了解处理涉及的数据结构类型,这可以通过 #include "vmlinux.h"
实现。在本中,我将详细介绍 vmlinux.h
是什么以及为什么你在编写 eBPF 程序开始就应该使用它。
2 1.2 vmlinux.h
概述
确切说,vmlinux.h
是使用工具生成的代码文件。它包含了系统运行 Linux 内核源代码中使用的所有类型定义。当我们编译 Linux 内核时,会输出一个称作 vmlinux
的文件组件,其是一个 ELF 的二进制文件,包含了编译好的可启动内核。vmlinux
文件通常也会被打包在主要的 Linux 发行版中。
内核中的 bpftool 工具其中功能之一就是读取 vmlinux
文件并生成对应的 vmlinux.h
头文件。vmlinux.h
会包含运行内核中所使用的每一个类型定义,因此该文件的比较大。
生成 vmlinux.h
文件的命令如下:
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
包含该 vmlinux.h
,就意味着我们的程序可以使用内核中使用的所有数据类型定义,因此 BPF 程序在读取相关的内存时,就可以映射成对应的类型结构按照字段进行读取。
例如,Linux 中的 task_struct 结构用于表示进程,如果 BPF 程序需要检查 task_struct
结构的值,那么首先就需要知道该结构的具体类型定义。
3 1.3 一处编译,到处运行
由于 vmlinux.h
文件是由当前运行内核生成的,如果你试图将编译好的 eBPF 程序在另一台运行不同内核版本的机器上运行,可能会面临崩溃的窘境。这主要是因为在不同的版本中,对应数据类型的定义可能会在 Linux 源代码中发生变化。
但是,通过使用 libbpf 库提供的功能可以实现 “CO:RE”(一次编译,到处运行)。libbpf 库定义了部分宏(比如 BPF_CORE_READ),其可分析 eBPF 程序试图访问 vmlinux.h
中定义的类型中的哪些字段。如果访问的字段在当前内核定义的结构中发生了移动,宏 / 辅助函数会协助自动找到对应字段【译者注:对于可能消失的字段,也提供了对应的辅助函数 bpf_core_field_exists】。因此,我们可以使用当前内核中生成的 vmlinux.h
头文件来编译 eBPF 程序,然后在不同的内核上运行它【译者注:需要运行的内核也支持 BTF 内核编译选项】。
4 1.4 总结
通过生成包含所有 Linux 内核类型的 vmlinux. h
头文件,我们可以在编写 eBPF 程序时对内核类型相关的头文件依赖。在下一篇文章中,我将使用 vmlinux.h
的便利性、libpf 的灵活性和 Go 的安全性来编写 eBPF 程序,– 敬请期待!【译者注:下一篇参见这里】
该文章首次出现在 Grant Seltzer 的博客上。
原文地址:https://blog.aquasec.com/vmlinux.h-ebpf-programs
作者:Grant Seltzer
时间: 2021 年 4 月 30 号
- 原文作者:DavidDi
- **原文链接:**https://www.ebpf.top/post/intro_vmlinux_h/
- **版权声明:**本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。