wxz*_*wxz 5 linux linux-kernel cgroups bpf ebpf
我对系统调用和函数eBPF中使用的术语非常困惑。有人可以分解我对加载/附加进程的理解是错误的吗?bpflibbpfbpf
我的理解:
我一直在查看代码,cgroups因此我将使用它作为示例。我知道 cgroup 的 bpf 程序存储在cgroup_bpf->effective[bpf_attach_type](请参阅此处和此处)。我知道,当从任何处理程序调用它们(例如在本例中为 function )调用类似__cgroup_bpf_run_filter_sysctl()(在cgroup.cproc_sys_call_handler()中)的函数时,这些程序就会被访问和运行。但是这些程序是如何加载/存储/附加/链接的,每个术语之间有什么区别?
加载- 显然,这需要在附加之前发生。我猜测这是 cgroup 程序存储在通用位置(即不在)cgroup_bpf->effective[bpf_attach_type]中的位置,因此尚未附加/链接以在内核中执行。因此,加载与bpf_attach_type. 如果我是正确的,cgroup 程序类型的通用加载位置在哪里?
附加- 也许这是程序与bpf_attach_type. 就像这样,现在我们采用通用存储的程序,现在我们可以cgroup_bpf->effective[bpf_attach_type]使用bpf_attach_type我们想要的任何内容将其放入数组中。然后,当到达内核中相应的附加点时,程序就会运行。
链接- 这只是一种特殊类型的附件吗?这与普通附加有何不同?我看到了link_create(),当使用 cgroup 程序类型时,两者最终都会调用。cgroup_bpf_prog_attach() cgroup_bpf_attach()
Qeo*_*ole 13
让我们来看看...
\n加载程序包括通过系统调用将其指令注入内核bpf(BPF_PROG_LOAD, ...)(对于大多数程序类型)。该程序通过了验证器,该验证器运行许多检查并可能重写一些指令(特别是对于地图访问)。如果启用了 JIT 编译,则程序可能会被 JIT 编译。内核内存中定义的程序是一个包含(或指向)有关程序的信息的对象,包括其 eBPF 字节码和(如果相关)JIT 编译的指令。struct bpf_prog
在此过程结束时,程序位于内核内存中。它不依附于特定的物体。它有一个引用计数器,内核会保留它直到计数器达到零。文件描述符可以保存对程序的引用:bpf()例如,文件描述符是由系统调用返回到加载应用程序的。其他引用可以通过附加、链接、固定程序或(如果我没记错的话)在 prog_array 映射中引用它来创建。如果没有保留任何引用(例如,加载应用程序在加载程序后立即退出,从而关闭其指向该程序的文件描述符),则它将从内核中删除。
\xe2\x80\x9cattach type\xe2\x80\x9d 的概念取决于程序类型。有些程序类型没有这个概念:XDP 程序只是附加到接口的 XDP 挂钩。附加到 cgroup 的程序确实有一个 \xe2\x80\x9cattach 类型 \xe2\x80\x9d 来准确说明程序附加的位置。
\n加载程序大多与这些附加类型分开。但是,某些程序类型(并非全部)确实要求用户在加载时通过传递给系统调用的对象expected_attach_type字段传递预期的附加类型。验证程序和系统调用处理程序使用此预期的附加类型来执行各种验证。union bpf_attrbpf()
您对附件步骤的理解听起来不错。根据其附加和/或程序类型,该程序被\xe2\x80\x9catched\xe2\x80\x9d 到它应该运行的挂钩。在您的情况下,相关的内核结构cgroup_bpf->effective将指向程序(而不是存储它 - 程序不会移动,cgroup_bpf->effective只是指向 的列表stuct bpf_prog *),并且在此挂钩上发生的事件将触发程序。
请注意,对于某些程序类型(例如网络或 cgroup 附加程序),附加程序会增加其引用计数器,以便加载应用程序可以退出,而无需从内核中删除程序。对于某些其他程序类型,例如 kprobes,这不足以保持程序打开,因为附加是基于 保持程序附加返回的文件描述符perf_event_open(),并且进程需要保持运行以保持此文件描述符打开。
当加载应用程序关闭时,我们如何保持 eBPF 探针运行?这就是 eBPF 链接发挥作用的地方。eBPF 程序可以附加到链接而不是传统的挂钩。链接本身附加到内核挂钩。这为操作程序提供了更好的界面。优点之一是可以固定此类链接,以便在加载应用程序退出时保持 eBPF 探针运行。另一个优点是可以更轻松地跟踪程序上保存的引用,并确保在加载应用程序意外退出时不会保持加载的 eBPF 程序。
\n链接\xe2\x80\x9c 是附件\xe2\x80\x9d 的特殊类型吗?我不太确定。查看代码,跟踪挂钩现在似乎始终与较新内核上的链接一起使用。对于其他程序类型,eBPF links 提供的接口是后来添加的,并且似乎与传统的钩子并存。例如,对于 cgroups,您可以以旧的方式附加程序(通过cgroup_bpf_prog_attach(),或者您可以加载它们,创建一个 eBPF 链接并将您的程序附加到该链接(通过link_create()) - 正如您所观察到的,在这两种情况下您最终都会运行cgroup_bpf_attach()。
我不认为目前有关于 eBPF 链接的良好文档,因此我们拥有的最好的可能是补丁集中的求职信和提交日志:
\n\n不要将 eBPF 链接与用于在加载到内核之前存储字节码的 ELF 对象文件的链接混淆。例如,libbpf 能够链接包含各种 eBPF 函数或子程序或其他对象的多个对象文件,并生成包含所有这些对象的单个输出 ELF 文件。这与 \xe2\x80\x9cbpf_link\xe2\x80\x9d 接口无关。
\n固定是一种保存对 eBPF 对象(程序、地图或链接)引用的方法。它是通过系统调用完成的,它在eBPF 虚拟文件系统bpf(BPF_OBJ_PIN, ...)中创建一个路径,并且稍后可以通过 -ing 该路径来检索对象的文件描述符。只要对象被固定,它就会保留在内核中。运行程序或地图不需要固定它。只要存在其他引用(文件描述符,或者程序附加到某些钩子;或者对于映射,它们被现有程序引用......),程序就会保持加载在内核内存中,如果附加,它就可以运行。open()
特别是,固定 eBPF 链接可确保附加到该链接的程序在其加载应用程序终止并关闭其文件描述符后仍保持加载状态。
\nexpected_attach_type 可能是必要的。