让我们看看...
加载中
加载程序包括通过bpf(BPF_PROG_LOAD, ...) 系统调用将其指令注入内核(对于大多数程序类型)。该程序通过了验证程序,验证程序运行许多检查并可能重写一些指令(特别是地图访问)。如果启用了 JIT 编译,那么程序可能会被 JIT 编译。内核内存中定义的程序是a struct bpf_prog object,包含(或指向)有关程序的信息,包括其eBPF字节码和(如果相关)JIT编译的指令。
在这个过程结束时,程序位于内核内存中。它不附加到特定对象。它有一个引用计数器,内核一直保存它,直到计数器变为零。引用可以由程序的文件描述符保存:例如,一个由bpf() 系统调用返回给加载应用程序。可以通过附加、链接、固定程序或(如果我没记错的话)在 prog_array 映射中引用它来创建其他引用。如果没有保留引用(例如,加载应用程序在加载程序后立即退出,从而关闭其指向程序的文件描述符),则将其从内核中删除。
“附加类型”的概念取决于程序类型。有些程序类型没有这个概念:XDP 程序只是附加到接口的 XDP 挂钩上。附加到 cgroups 的程序确实有一个“附加类型”,它可以准确地告诉程序附加到哪里。
加载程序大多与这些附加类型分开。但是,某些程序类型——不是全部——do require the user to pass the expected attach type at load time,通过union bpf_attr 对象的expected_attach_type 字段传递给bpf() 系统调用。验证器和系统调用处理程序使用这种预期的附加类型来执行各种验证。
附加
您对附件步骤的理解听起来不错。根据它的附加和/或程序类型,程序被“附加”到它应该运行的钩子上。相关的内核结构,在您的情况下为cgroup_bpf->effective,将指向程序(不是 store 它 - 程序不会移动,cgroup_bpf->effective 只是指向stuct bpf_prog * 的列表),并且在这个钩子上发生的事件将触发程序。
请注意,对于某些程序类型,例如网络或 cgroup 附加程序,附加程序会增加其引用计数器,以便加载应用程序可以退出而不会从内核中删除程序。对于其他一些程序类型,例如kprobes,这不足以保持程序打开,因为附加是基于perf_event_open()返回的文件描述符来保持程序附加,并且需要一个进程保持运行来保存这个文件描述符打开。
链接
当加载应用程序关闭时,我们如何保持 eBPF 探针运行?这就是 eBPF 链接发挥作用的地方。 eBPF 程序可以附加到链接而不是传统的钩子。链接本身附加到内核挂钩。这为操作程序提供了更好的界面。一个优点是可以pin这样的链接,以在加载应用程序退出时保持 eBPF 探针运行。另一个优点是更容易跟踪程序中保存的引用,并确保在加载应用程序意外退出时没有 eBPF 程序保持加载。
链接是“特殊类型的附件”吗?我不确定。查看代码,似乎跟踪钩子现在总是与较新内核上的链接一起使用。对于其他程序类型,由 eBPF 链接提供的接口是后来添加的,似乎与传统的挂钩旁边存在。例如,对于 cgroups,您可以以旧方式附加程序(通过cgroup_bpf_prog_attach(),或您可以加载它们,创建一个 eBPF 链接并将您的程序附加到该链接(通过link_create()) -正如您所观察到的,在这两种情况下,您最终都会运行cgroup_bpf_attach()。
我认为目前没有关于 eBPF 链接的好的文档,所以我们拥有的最好的可能是补丁集中的求职信和提交日志:
不要将 eBPF 链接与用于在字节码加载到内核之前存储字节码的 ELF 目标文件的链接相混淆。例如,libbpf 能够链接多个包含各种 eBPF 函数或子程序或其他对象的对象文件,并生成包含所有这些对象的单个输出 ELF 文件。这与“bpf_link”接口无关。
固定
Pinning 是一种保存对 eBPF 对象(程序,也包括映射或链接)的引用的方法。它是通过bpf(BPF_OBJ_PIN, ...) 系统调用完成的,它在the eBPF virtual file system 中创建一个路径,并且稍后可以通过open()-ing 该路径检索对象的文件描述符。只要一个对象被固定,它就保留在内核中。不需要固定程序或地图来运行它。只要存在其他引用(文件描述符,或者程序附加到某些挂钩;或者对于映射,它们被现有程序引用......),程序就会保持加载在内核内存中,如果附加,它可以运行。
特别是,固定 eBPF 链接可确保附加到该链接的程序在其加载应用程序终止并关闭其文件描述符后仍保持加载状态。
总结
-
加载: 将程序注入内核,验证器启动,它可能会重写一些指令并链接到相关的内部 eBPF 对象(BTF、地图等),并且可能会发生 JIT 编译。
expected_attach_type 字段可能是必需的。
-
附加:程序附加到与其程序类型相关的钩子上,如果相关,使用提供的附加类型。
-
链接:根据程序类型或如果需要,程序会附加到 eBPF 链接,而不是直接附加到其常规附加点。该链接附加到常规挂钩,并提供更灵活的界面来管理程序。
-
固定:程序或链接(或地图)可以固定到 bpff 以使其持久化(但不能在重新启动后)。