第 3 章. eBPF 计划剖析
本作品已使用人工智能进行翻译。欢迎您提供反馈和意见:translation-feedback@oreilly.com
在上一章中,你看到了一个使用 BCC 框架编写的简单 eBPF "Hello World "程序。本章将提供一个完全用 C 语言编写的 "Hello World "程序示例,让你了解 BCC 在幕后处理的一些细节。
本章还向你展示了 eBPF 程序从源代码到执行过程中所经历的各个阶段,如图 3-1 所示。
图 3-1. C 语言(或 Rust)源代码被编译成 eBPF 字节码,再经过 JIT 编译或解释成本地机器码指令
eBPF 程序是一组 eBPF 字节码指令。可以直接用字节码编写 eBPF 代码,就像用汇编语言编程一样。人类通常认为高级编程语言更容易处理,至少在本文写作时,我认为绝大多数 eBPF 代码都是用 C 语言编写的1编写,然后编译成 eBPF 字节码。
从概念上讲,这种字节码在内核中的 eBPF 虚拟机中运行。
eBPF 虚拟机
eBPF 虚拟机与其他虚拟机一样,是计算机的软件实现。它以 eBPF 字节码指令的形式接收程序,并将其转换为在 CPU 上运行的本地机器指令。
在 eBPF 的早期实现中,字节码指令是在内核中解释的,也就是说,每次运行 eBPF 程序时,内核都会检查指令并将其转换为机器码,然后执行机器码。由于性能原因,以及为了避免 eBPF 解释器中可能存在的一些与 Spectre 相关的漏洞,解释工作在很大程度上已被 JIT(即时编译)所取代。编译意味着只需在程序加载到内核时进行一次本地机器指令的转换。
eBPF 字节码由一组指令组成,这些指令作用于(虚拟)eBPF 寄存器。eBPF 指令集和寄存器模型的设计与常见的 CPU 架构完全一致,因此从字节码编译或解释为机器码的步骤相当简单。
eBPF 寄存器
eBPF 虚拟机使用 10 个通用寄存器,编号为 0 至 9。此外,寄存器 10 用作堆栈帧指针(只能读取,不能写入)。在执行 BPF 程序时,这些寄存器中会存储数值,以便跟踪状态。
需要了解的是,eBPF 虚拟机中的这些 eBPF 寄存器是通过软件实现的。你可以在 Linux 内核源代码的include/uapi/linux/bpf.h头文件中看到它们从BPF_REG_0 到BPF_REG_10 的枚举。
eBPF 程序的上下文参数在执行开始前被载入寄存器 1。函数的返回值存储在寄存器 0 中。
在调用 eBPF 代码中的函数之前,该函数的参数会被放入寄存器 1 至寄存器 5 中(如果参数少于 5 个,则不会使用所有寄存器)。
Become an O’Reilly member and get unlimited access to this title plus top books and audiobooks from O’Reilly and nearly 200 top publishers, thousands of courses curated by job role, 150+ live events each month,
and much more.
Read now
Unlock full access