💡 BPF映射如何创建
程序员经常向程序传递消息引起程序行为被调用。bpf 最神奇的功能就是内核中运行的代码,和加载这些代码的程序可以通过消息传递方式实现实时通信。
BPF 映射是键值保存在内核中,可以被 BPF 程序访问。用户空间的程序也可以通过文件描述符访问 bpf 映射。bpf 映射中可以保存事先指定的大小任何类型数据。
内核将键值作为二进制块,内核并不关系 bpf 映射的具体内容,由验证器来保证安全。
创建 BPF 映射
创建 BPF 映射最直接的方法是使用 bpf 系统调用,第一个参数设置为 BPF_MAP_CREATE,则表示创建一个新的映射。
该调用将返回与创建映射相关的文件描述符。bpf 系统调用的第二个参数如下所示:
https://man7.org/linux/man-pages/man2/bpf.2.htm[1]
BPF_PROG_LOAD
Verify and load an eBPF program, returning a new file
descriptor associated with the program. The close-on-exec
file descriptor flag (seefcntl(2)) is automatically
enabled for the new file descriptor.
Thebpf_attr union consists of various anonymous
structures that are used by differentbpf() commands:
union bpf_attr {
struct { /* Used by BPF_MAP_CREATE */
__u32 map_type;
__u32 key_size; /* size of key in bytes */
__u32 value_size; /* size of value in bytes */
__u32 max_entries; /* maximum number of entries
in a map */
};
struct { /* Used by BPF_MAP_*_ELEM and BPF_MAP_GET_NEXT_KEY
commands */
__u32 map_fd;
__aligned_u64 key;
union {
__aligned_u64 value;
__aligned_u64 next_key;
};
__u64 flags;
};
struct { /* Used by BPF_PROG_LOAD */
__u32 prog_type;
__u32 insn_cnt;
__aligned_u64 insns; /* 'const struct bpf_insn *' */
__aligned_u64 license; /* 'const char *' */
__u32 log_level; /* verbosity level of verifier */
__u32 log_size; /* size of user buffer */
__aligned_u64 log_buf; /* user supplied 'char *'
buffer */
__u32 kern_version;
/* checked when prog_type=kprobe
(since Linux 4.1) */
};
} __attribute__((aligned(8)));
如果系统调用失败,内核返回-1;失败的原因主要有三种:属性无效 EINVAL,没有足够的执行权限 EPERM,没有足够的内存保存映射 ENOMEM。
使用 ELF 约定创建 BPF 映射
内核中包括一些约定和帮助函数,用于生成和使用 BPF 映射。
如帮助函数bpf_create_map
封装了上面的代码。
可以预先定义映射:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size= sizeof(int),
.value_size=sizeof(int),
.max_entries=100,
.map_flags = BPF_F_NO_PREALLOC,
};
这种方式使用section
属性来定义映射,本示例中为SEC(“maps”)
。告诉内核结构时 bpf 映射,并告诉内核创建相应的映射。
映射初始化后,就可以使用他们在内核和用户空间之间传递消息。
#include <errno.h>
#include <linux/bpf.h>
#include <stdio.h>
#include <string.h>
#include "bpf.h"
static const char *file_path = "/sys/fs/bpf/my_array";
int main(int argc, char **argv) {
int key, value, fd, added, pinned;
fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(int), 100, 0);
if (fd < 0) {
printf("Failed to create map: %d (%s)\n", fd, strerror(errno));
return -1;
}
key = 1, value = 1234;
added = bpf_map_update_elem(fd, &key, &value, BPF_ANY);
if (added < 0) {
printf("Failed to update map: %d (%s)\n", added, strerror(errno));
return -1;
}
pinned = bpf_obj_pin(fd, file_path);
if (pinned < 0) {
printf("Failed to pin map to the file system: %d (%s)\n", pinned,
strerror(errno));
return -1;
}
return 0;
}
💡 小贴士:calico:Here’s @TomkinsDA 's #FOSDEM 2022 talk titled ‘2-cluster #Kubernetes, with #Calico, #BGP Interconnect and #WireGuard… All Without Leaving Your Laptop!’… Stay tuned for the fun surprise at the end.
https://video.fosdem.org/2022/D.network/2_cluster_kubernetes_with_calico_bgp_interconnect_and_wireguard_all_without_leaving_your_laptop.webm[2]
参考资料
[1]
https://man7.org/linux/man-pages/man2/bpf.2.htm: https://man7.org/linux/man-pages/man2/bpf.2.htm
[2]
https://video.fosdem.org/2022/D.network/2_cluster_kubernetes_with_calico_bgp_interconnect_and_wireguard_all_without_leaving_your_laptop.webm: https://man7.org/linux/man-pages/man2/bpf.2.htm