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/