写在前面
BPF CO-RE 即Compile Once – Run Everywhere,编译一次——到处运行。BPF CO-RE是为了解决BPF的可移植性而存在,也就是说编写一个BPF程序,该程序能够成功编译、通过内核验证,并且能够在不同的内核版本上正确运行,而无需为每个特定的内核重新编译。
一,BPF可移植性问题
BPF程序是一段用户提供的代码,直接注入到内核中。一旦加载和验证,BPF程序将在内核上下文中执行。这些程序在内核内存空间中运行,可以访问所有可用的内部内核状态。这是非常强大的,也是BPF技术在如此多不同的应用程序中成功使用的原因之一。然而,这种强大的能力也带来了我们今天所面临的BPF可移植性问题:BPF程序无法控制周围内核环境的内存布局。它们必须与独立开发、编译和部署的内核所提供的内容一起工作。
此外,内核类型和数据结构总是在不断变化。不同的内核版本会将结构体内部的字段重新排序,甚至移动到新的内部结构体中。字段可能会被重命名或删除,它们的类型可能会被更改,要么变成一些轻微兼容的类型,要么是完全不同的类型。结构体和其他类型可能会被重命名,或者根据内核配置条件性地编译掉,或者在内核版本之间被完全移除。
换句话说,内核发布之间总是会有变化,但BPF应用程序开发人员却需要以某种方式应对这个问题。考虑到这种不断变化的内核环境,今天怎么可能用BPF做任何有用的事情呢?有几个原因可以解释这一点。
首先,并不是所有的BPF程序都需要查看内部的内核数据结构。一个例子是opensnoop工具,它依赖于kprobes/tracepoints来跟踪哪些进程打开了哪些文件,只需要捕获几个系统调用参数就可以工作。由于系统调用参数提供了一个稳定的ABI,这些参数在内核版本之间不会改变,因