Linux kernel 谁在BPF中创建地图
在阅读了Linux kernel 谁在BPF中创建地图,linux-kernel,bpf,ebpf,Linux Kernel,Bpf,Ebpf,在阅读了manbpf和其他一些文档源之后,我的印象是map只能由用户进程创建。然而,下面的小程序似乎神奇地创建了bpfmap: struct bpf_map_def SEC("maps") my_map = { .type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(u32), .value_size = sizeof(long), .max_entries = 10, }; SEC("soc
manbpf
和其他一些文档源之后,我的印象是map
只能由用户进程创建。然而,下面的小程序似乎神奇地创建了bpf
map:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(long),
.max_entries = 10,
};
SEC("sockops")
int my_prog(struct bpf_sock_ops *skops)
{
u32 key = 1;
long *value;
...
value = bpf_map_lookup_elem(&my_map, &key);
...
return 1;
}
因此,我使用内核的工具/bpf/bpftool
加载程序,并验证程序是否已加载:
$ bpftool prog show
1: sock_ops name my_prog tag f3a3583cdd82ae8d
loaded_at Jan 02/18:46 uid 0
xlated 728B not jited memlock 4096B
$ bpftool map show
1: array name my_map flags 0x0
key 4B value 8B max_entries 10 memlock 4096B
当然地图是空的。但是,从程序中删除bpf\u map\u lookup\u elem
将导致不创建映射
更新
我用strace
对它进行了调试,发现在这两种情况下,即使用bpf\u map\u lookup\u elem
和不使用它时,bpftool都会调用bpf(bpf\u map\u CREATE,…)
,显然成功了。然后,如果省略了bpf_map_lookup_elem,我将选择bpftool map show
,并且bpf(bpf_map_GET_NEXT_ID,…)
立即返回enoint
,它永远不会转储映射。所以很明显,有些东西没有完成地图的创建
所以我想知道这是否是预期的行为
谢谢
我的印象是,地图只能由用户进程创建
您完全正确-用户程序是调用bpf
系统调用以加载eBPF程序和创建eBPF映射的程序
你就这么做了:
所以我用tools/bpf/bpftool加载程序,然后
您的程序是调用bpf
syscall的用户进程,因此也是创建eBPF映射的用户进程
当创建BPF程序的用户程序退出时,不必卸载BPF程序-bpftool可能使用此机制
连接点的一些相关位:
一个用户进程可以创建多个映射。。。并通过文件描述符访问它们
通常,eBPF程序由用户进程加载,并在进程退出时自动卸载。在某些情况下。。。即使加载程序的进程退出,该程序仍将在内核中保持活动状态
每个eBPF程序都是一组指令,在其完成之前可以安全运行。。。在验证过程中,内核会增加eBPF程序使用的每个映射的引用计数,以便在卸载程序之前无法删除附加的映射
正如antiduh所解释的,并通过您的
strace
检查确认,bpftool
是在这种情况下创建地图的用户空间程序。它从libbpf(在tools/lib/bpf/
下)调用函数bpf\u prog\u load()
,然后执行系统调用。然后将程序固定在所需位置(在bpf
虚拟文件系统装载点下),以便在bpftool返回时不会卸载程序。地图没有固定
关于地图的创建,魔法位也发生在libbpf中。调用bpf\u prog\u load()
时,libbpf将接收对象文件的名称作为参数bpftool
不要求加载此特定程序或该特定映射;相反,它提供了对象文件,libbpf必须处理它。因此libbpf中的函数解析这个ELF对象文件,并最终找到与映射和程序相对应的多个部分。然后它尝试加载第一个程序
加载此程序包括以下步骤:
CHECK_ERR(bpf_object__create_maps(obj), err, out);
CHECK_ERR(bpf_object__relocate(obj), err, out);
CHECK_ERR(bpf_object__load_progs(obj), err, out);
换句话说:首先创建在对象文件中找到的所有贴图。然后执行映射重新定位(即将映射索引与eBPF指令关联),最后加载程序指令
关于您的问题:在这两种情况下,无论是否使用bpf\u map\u lookup\u elem()
,映射都是使用bpf(bpf\u map\u CREATE,…)
syscall创建的。在这之后,重新定位发生了,并且如果需要,调整程序指令以指向新创建的地图。完成所有步骤并加载程序后,bpftool
退出。eBPF程序应该被固定,并且仍然加载到内核中。据我所知,如果它确实使用了映射(如果使用了bpf\u map\u lookup\u elem()
),那么映射仍然由加载的程序引用,并保存在内核中。另一方面,如果程序不使用映射,那么就没有什么可以阻止它们了,因此当bpftool
持有的文件描述符关闭时,当bpftool
返回时,映射将被销毁
因此,最后,当
bpftool
完成时,如果程序使用映射,则在内核中加载了一个映射,但是如果没有程序依赖它,则没有映射。在我看来,这似乎是意料之中的行为;但是,如果您在使用bpftool
时遇到奇怪的事情,请以这种或那种方式执行ping,我是该实用程序的开发人员之一。最后一个一般性观察:映射也可以被固定并保留在内核中,即使没有程序使用它们,如果需要保留它们的话。感谢您的评论,但是它没有解释为什么将bpf\u map\u lookup\u elem
排除在bpf程序之外不会创建映射。谢谢您的全面回答!为了完全卸载固定的BPF程序,只需umount/sys/fs/BPF/xxx
,对吗?这是否保证bpf blob将从内存中删除?您将rm
固定的条目和umount
eBPF虚拟FS。删除:我认为这取决于你的程序是否还在使用。如果连接到(例如)TC,然后锁定程序,然后删除锁定条目,则程序应仍然存在并连接,直到您删除TC分类器。如果您已经分离程序(或者如果它从未附加到任何地方,例如,如果您加载了bpftool
),那么是的,删除/sys/fs/bpf
下的条目就足够了。哦,我明白了,我想如果程序已附加到cgroup
中,我应该首先使用bpftool
分离它,然后删除/sys/