问题描述
因此,我正在研究 samples/bpf/*
示例,并在最近使用 libbpf
的代码中发现了以下模式:
struct bpf_link *links[PROGS_NUM] = { NULL,};
struct bpf_program *prog;
struct bpf_object *obj;
int i = 0;
obj = bpf_object__open_file(filename,NULL);
bpf_object__load(obj);
bpf_object__for_each_program(prog,obj) {
links[i] = bpf_program__attach(prog);
i++;
}
现在,在附加程序之后,是否可以关闭 bpf
对象实例,例如bpf_object__close(obj)
或 obj
和 links
必须存在并且只要程序在内核中加载就可以访问?
解决方法
我没有运行特定测试来回答您的问题,但根据我的理解:“这取决于”,尤其是程序类型。您可能会关闭 obj
,但如果您也关闭 links
,那么当您的用户空间加载程序终止时,跟踪 eBPF 程序可能会被分离和卸载。
eBPF 程序生命周期
一旦加载,eBPF 程序就会留在内核中,只要它的引用计数器保持严格的正数。有许多“句柄”可以保存对程序的引用:
- 将程序附加到钩子(例如 TC 过滤器或内核探测器)会增加计数器。
- 在加载程序或向加载的程序请求文件描述符时从内核返回的文件描述符也包含引用。
- eBPF 虚拟文件系统中的固定路径做同样的事情。
- 在
BPF_MAP_TYPE_PROG_ARRAY
映射中引用程序(用于尾调用)也包含一个引用。
当所有这些句柄都消失时 - 当程序分离时,加载它的用户应用程序终止,并且它没有固定到 bpffs,然后程序被卸载。
eBPF 链接
所以我们说附加程序会增加它的引用计数器,这意味着只要程序被附加,它就会保持加载状态。例如,对于 TC 过滤器或 XDP 程序,这使事情变得更容易,因为用户应用程序可以附加程序并安全终止 - 程序保持附加和加载状态。对于跟踪,附加探测器通常是通过调用 perf_event_open()
来完成的,检索文件描述符(与加载 eBPF 程序时获得的描述符不同),并使用它与 ioctl()
进行附加。当这个文件描述符
已关闭,程序已分离[注意:这是我对附加探针的基本理解,也许我错过了一些东西,可能还有其他解决方案]。因此,当用户应用程序终止时,两个文件描述符(来自加载和附加)都将关闭,同时程序被分离和卸载。固定程序可防止卸载,但不会分离(因此程序已加载到内核中但从未运行)。
作为一种变通方法,引入了 eBPF 链接以提供更好的用户附加程序体验,使其更容易保持附加状态,并在管理附加/分离时更加一致。 struct bpf_link
引用附加程序时获得的文件描述符。当用户应用程序终止时,可以固定链接以保持持久性,从而确保探针保持活动状态。
obj
和 links
就您而言,如果关闭 obj
和 links
会发生什么?
obj
是一个(指向 a)struct bpf_object
的指针,它的内部由 libbpf 对用户隐藏。它是从一个目标文件构建的,并在加载包含在该目标文件中的 eBPF 程序时更新。它包含指向带有 struct bpf_program
的 instances
对象的指针,最后是 fds
,即加载程序时获得的文件描述符。如果我们关闭它们(通过 bpf_object__close()
,调用 bpf_object__unload()``,and in turn
bpf_program__unload()),those handles for keeping the program are gone. This is not an issue,as long as other references are kept elsewhere - for example,if the program is attached. So I _think_ that
obj` 应该可以安全关闭。
如果我们也关闭 links
,我们也失去了固定链接的可能性。该进程仍然持有来自 perf_event_open()
的文件描述符,但它会在退出时关闭它。如果 eBPF 程序是一个跟踪程序,它将立即被分离和卸载。如果它是一个网络程序,它应该保持连接。
所以这完全取决于您的程序类型以及您是否希望程序继续运行。对于跟踪和监控用例,固定的 eBPF 链接允许您即使在用户空间加载器应用程序退出时也能继续探测。因此,在删除它们之前,可能需要仔细检查您不再需要那些 links
:)。