API 命名约定

libbpf API 提供了对一些逻辑上分离的函数和类型组的访问。每个组都有其自己的命名约定,在此处描述。建议在添加新函数或类型时遵循这些约定,以保持 libbpf API 的简洁和一致性。

libbpf API 提供的所有类型和函数都应具有以下前缀之一: bpf_, btf_, libbpf_, btf_dump_, ring_buffer_, perf_buffer_

系统调用包装器

系统调用包装器是 sys_bpf 系统调用支持的命令的简单包装器。这些包装器应放入 bpf.h 头文件中,并与相应的命令一一映射。

例如,bpf_map_lookup_elem 包装 sys_bpf 的 BPF_MAP_LOOKUP_ELEM 命令,bpf_prog_attach 包装 BPF_PROG_ATTACH,等等。

对象

libbpf API 提供的另一种类型和函数是“对象”和用于处理它们的功能。对象是高级抽象,例如 BPF 程序或 BPF 映射。它们由相应的结构表示,例如 struct bpf_objectstruct bpf_programstruct bpf_map 等。

结构体被前向声明,并且对其字段的访问应该通过相应的 getter 和 setter 提供,而不是直接访问。

这些对象与包含已编译 BPF 程序的 ELF 对象的相应部分相关联。

例如,struct bpf_object 表示从 ELF 文件或缓冲区创建的 ELF 对象本身,struct bpf_program 表示 ELF 对象中的程序,而 struct bpf_map 是一个映射。

使用对象的功能具有从对象名称、双下划线和描述功能目的的部分构建的名称。

例如,bpf_object__open 由相应对象的名称 bpf_object、双下划线和 open 组成,后者定义了打开 ELF 文件并从中创建 bpf_object 的功能目的。

除 BTF 相关之外的所有对象和相应的功能都应放入 libbpf.h 中。BTF 类型和函数应放入 btf.h 中。

辅助功能

不适合上述任何类别的辅助函数和类型应具有 libbpf_ 前缀,例如 libbpf_get_errorlibbpf_prog_type_by_name

ABI

libbpf 既可以静态链接,也可以用作 DSO。为了避免与应用程序链接的其他库发生可能的冲突,所有非静态 libbpf 符号都应具有上述 API 文档中提到的前缀之一。请参阅 API 命名约定,为新符号选择正确的名称。

符号可见性

libbpf 遵循以下模型:默认情况下,所有全局符号都具有“隐藏”可见性,并且要使符号可见,必须使用 LIBBPF_API 宏显式标记。例如:

LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);

这可以防止意外导出不应成为 ABI 一部分的符号,从而改善 libbpf 开发人员和用户体验。

ABI 版本控制

为了使将来的 ABI 扩展成为可能,libbpf ABI 已版本化。版本控制是通过传递给链接器的 libbpf.map 版本脚本来实现的。

版本名称是 LIBBPF_ 前缀 + 三组件数字版本,从 0.0.1 开始。

每次 ABI 发生更改时,例如,因为添加了新符号或更改了现有符号的语义,都应增加 ABI 版本。ABI 版本的增加最多在每个内核开发周期发生一次。

例如,如果 libbpf.map 的当前状态为

LIBBPF_0.0.1 {
        global:
                bpf_func_a;
                bpf_func_b;
        local:
                \*;
};

,并且正在引入新符号 bpf_func_c,则应像这样更改 libbpf.map

LIBBPF_0.0.1 {
        global:
                bpf_func_a;
                bpf_func_b;
        local:
                \*;
};
LIBBPF_0.0.2 {
        global:
                bpf_func_c;
} LIBBPF_0.0.1;

,其中新版本 LIBBPF_0.0.2 依赖于先前的 LIBBPF_0.0.1

版本脚本的格式以及处理 ABI 更改(包括不兼容的更改)的方法在 [1] 中有详细说明。

独立构建

https://github.com/libbpf/libbpf 下,有一个(半)自动化的主线版本 libbpf 的镜像,用于独立构建。

但是,对 libbpf 代码库的所有更改都必须通过主线内核树向上游推送。

API 文档约定

libbpf API 通过头文件中定义上方的注释进行文档化。这些注释可以由 doxygen 和 sphinx 呈现,以获得组织良好的 html 输出。本节描述了这些注释应采用的格式约定。

以下是 btf.h 中的示例:

/**
 * @brief **btf__new()** creates a new instance of a BTF object from the raw
 * bytes of an ELF's BTF section
 * @param data raw bytes
 * @param size number of bytes passed in `data`
 * @return new BTF object instance which has to be eventually freed with
 * **btf__free()**
 *
 * On error, error-code-encoded-as-pointer is returned, not a NULL. To extract
 * error code from such a pointer `libbpf_get_error()` should be used. If
 * `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)` is enabled, NULL is
 * returned on error instead. In both cases thread-local `errno` variable is
 * always set to error code as well.
 */

注释必须以 ‘/**’ 形式的块注释开头。

文档始终以 @brief 指令开头。此行是有关此 API 的简短描述。它以 API 的名称开头,并以粗体显示,如下所示: api_name。如果这是一个函数,请包括一个开括号和一个闭括号。紧接着是 API 的简短描述。可以在最后一个指令下方的注释底部添加更长的描述。

参数用 @param 指令表示,每个参数应有一个。如果这是一个具有非 void 返回值的函数,请使用 @return 指令来记录它。

许可

libbpf 在 LGPL 2.1 和 BSD 2-Clause 双重许可下获得许可。