用 Rust 写 eBPF 程序
Contents
基本原理
用 Rust 写 eBPF 程序本质上还是依赖于 libbpf 的能力。因为 Rust 的 FFI 可以使用 C 的 ABI,所以 libbpf 所暴露的 API 可以以 Library 的形式直接给 Rust 调用。
实际上,libbpf-sys 这个项目就给 libbpf 包了一层 Rust 的 API,目前几个用 Rust 写 eBPF 程序的项目都是基于这个项目再实现更上层的 API。
我们可以直接用 libbpf-rs 这个项目,这个项目是 libbpf 项目开发的一个未成熟的项目。这个项目其实是由两个子项目组成(通过 Cargo Workspace 来组织):
-
libbpf-cargo Cargo 的子命令
libbpf
,编译将生成一个 binary。这个子命令主要用来编译*.bpf.c
和自动生成 skeleton 代码; -
libbpf-rs 基于 libbpf-sys 开发的更上层的接口。
用 Rust 写 eBPF 程序其实只是用更上层和抽象的 API 来写用户层代码,内核加载的 eBPF 程序还是用 C 语言编写(一般这些代码比较短,逻辑相对简单很多)。
工作流程
备注:内核要支持 CO-RE 特性。
1. 安装 Cargo 的子命令 libbpf-cargo
Cargo 提供了子命令扩展机制。扩展的子命令本质上也是一个独立的 binary,也可以托管在 crates.io 上:
|
|
安装完之后,我们可以执行:
|
|
看是否有 libbpf
这个命令。
2. 创建一个 Rust 项目
|
|
在 bpf/
下创建 runqslower.bpf.c
,并补充对应的 headers(可参考 examples),最终的目录结构如下所示:
|
|
注意 *.bpf.c
的后缀是必须的,表示这是一个将要被内核加载的 eBPF 程序。
3. 使用 libbpf 子命令来编译 eBPF 程序并生成 skeleton 代码
|
|
4. 编译用户层代码
在 main.rs
添加对应的逻辑,可参考 main.rs
。
5. 编译完整代码并执行
|
|
此时完整的命令行 runqslower-rs
已经通过 skeleton 代码将对应的 object 嵌入了,所以直接执行就完成加载和运行的动作:
|
|