BPF的可移植性和CO-RE (Compile Once – Run Everywhere) (2)

迄今为止,人们编译这类BPF程序会依赖BCC (BPF Compiler Collection)。使用BCC,可以将BPF程序的C代码以字符串的形式嵌入到用户空间的程序中,当程序最终部署并运行在目标主机上后,BCC会唤醒其嵌入的Clang/LLVM,提取本地内核头文件(必须确保已从正确的kernel-devel软件包中将其安装在系统上),并即时进行编译。通过这种方式来确保BPF程序期望的内存布局和主机运行的内核的内存布局是相同的。如果需要处理一些选项和内核编译出来的潜在产物,则可以在自己的源代码中添加#ifdef/#else来适应重命名字段、不同的数值语义或当前配置导致的不可用内容等带来的风险。嵌入的Clang会移除代码中无关的内容,并调整BPF程序代码,以匹配到特定的内核。

这种方式听起来很不错,但实际并非没有缺点:

Clang/LLVM组合是一个很大的库,导致发布的应用的库会比较大

Clang/LLVM组合使用的资源比较多,因此当编译的BPF代码启动时会消耗大量资源,可能会推翻已均衡的生产负载

这样做其实也是在赌目标系统将存在内核头文件,大多数情况下这不是问题,但有时可能会引起很多麻烦。这也是内核开发人员感到特别麻烦的要求,因为他们经常必须在开发过程中构建和部署自定义的一次性内核。如果没有自定义构建的内核头文件包,则基于BCC的应用将无法在这种内核上运行,从而剥夺了开发人员用于调试和监视的工具集。

BPF程序的测试和开发迭代也相当痛苦,因为一旦重新编译并重启用户空间控制应用程序,甚至会在运行时遇到各种琐碎的编译错误。这无疑会增加难度,且无益于快速迭代。

总之, BCC是一个很好的工具,尤其适用于快速原型制作,实验和小型工具,但在用于广泛部署的生产BPF应用程序时,它无疑具有很多缺点。

我们正在使用BPF CO-RE来增强BPF的可移植性,并相信这是未来BPF程序开发的趋势,尤其是对于复杂的实际应用的BPF程序。

高级BFP CO-RE机制

BPF CO-RE在软件堆栈的各个级别汇集了必要的功能和数据:内核,用户空间的BPF加载器库(libbpf),和编译器(Clang)。通过这些组件来支持编写可移植的BPF程序,使用相同的预编译的BPF程序来处理不同内核之间的差异。BPF CO-RE需要以下组件的集成和合作:

BTF类型信息,用于允许获取关于内核和BPF程序类型和代码的关键信息,进而为解决BPF CO-RE的其他难题提供了可能性;

编译器(Clang)为BPF程序C代码提供了表达意图和记录重定位信息的方法;

BPF加载器(libbpf)将内核和BPF程序中的BTF绑定在一起,用于将编译后的BPF代码调整为目标主机上的特定内核代码;

内核,在完全不依赖BPF CO-RE的情况下,提供了高级BPF功能来启用某些更高级的场景。

这些组件可以集成到一起工作,提供前所未有的便捷性,适应性和表达性(来开发可移植BPF程序,以前只能在运行时通过BCC编译BPF程序的C代码来实现),而无需像BCC一样付出高昂的代价。

BTF

整个BPF CO-RE方法的关键推动因素之一是BTF。BTF (BPF Type Format) 是作为一个更通用,更详细的DWARF调试信息的替代品而创建的。BTF是一种节省空间,紧凑但仍具有足够表达能力的格式,可以描述C程序的所有类型信息。由于其简单性和使用的重复数据删除算法,与DWARF相比,BTF的大小可减少多达100倍。现在,已经可以在内核运行时使用显示地嵌入BPF类型信息:只需要启用CONFIG_DEBUG_INFO_BTF=y内核选项即可。内核本身可以使用BTF功能,用于增强BPF验证程序自身的功能。

关于BPF CO-RE更重要的是,内核还通过/sys/kernel/btf/vmlinux上的sysfs公开了这种自描述的权威BTF信息(定义了确切的结构布局)。尝试如下命令:

$ bpftool btf dump file /sys/kernel/btf/vmlinux format c

某些unix系统下安装的bpftool默认不支持btf命令选项,可以在linux内核源码的/tools/bpf/bpftool目录下执行make命令进行编译。如果遇到linux/if.h和net/if.h头文件定义冲突的话,可以将/tools/bpf/bpftool/net.c中的这一行注释掉再编译:

#include <linux/if.h>

目前很多内核默认并不会打开对BTF内核选项,因此需要自己编译内核。基本步骤如下:

首先升级gcc;

编译带BTF选项的内核前需要安装pahole,可以从github官方下载源码编译即可。需要注意的是,该编译过程需要依赖git,因此需要通过git clone代码编译,而不能下载源码压缩包编译

将导出当前系统配置:

$ cd linux-5.10.1 $ cp -v /boot/config-$(uname -r) .config

在linux-5.10.1目录中使用make menuconfig命令修改系统配置文件,并保存。可以使用"http://www.likecs.com/"直接查找需要修改的内核选项;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfpjy.html