xdp-tutorial-Base
默- xdp-tutorial 教程中 base1-4 笔记
- 资料来源:
- https://github.com/xdp-project/xdp-tutorial
- 更新
导语
xdp-tutorial 是非常好的 xdp 教程, 这一节先从入门开始.
流水账, 非翻译, 只记录了部分内容.
Base
01最简单的 xdp 程序, 通过所有流量.编译后是 xdp_pass_kern.o
- section xdp 的名字是
SEC("xdp")
加载 xdp 程序当然可以使用 ip 命令
ip 命令加载是直接加载到 nic, 因此一次 nic 只能加载一个 xdp 程序, 但是 xdp-loader 实现了 xdp nult-dispatch 更加强大更加丰富
自然也有使用用户程序加载, 简单来说是 libxdp
xdp_program__create->xdp_program__attachxdp_program__fd获取程序的 fd
- 这里警告似乎是无所谓;
- 两次加载的 Prio 是完全相同的, 使用其他加载方式也是一样
xdp_multiprog__detach卸载全部的 api
02
一个 elf 文件可以包含多个 xdp 程序, 使用 libxdp 选择需要加载的程序.
libbpf libxdp 提供了系统调用的封装和对应的操作 api
在 libxdp 内附加到一个 nic 的 xdp 程序以 struct xdp_program 表示;
xdp_program_optsxdp_program__create()创建xdp_program__bpf_obj(prog)转换为bpf_obj
02
先初始化程序
testenv.sh可以做到的事情实在是太多了. 初始化多个测试环境, 切换不同的测试环境 xxx- 应该是通过不同的网络命名空间隔离, 使用 veth 连接;
xdp_prog_kern.c 是使用 libxdp 加载卸载 xdp 程序的示例, 几个需要留意的点
小结: 本节介绍了 bpf 程序中存储机制, 如何在 kernel userspace 中共享/修改数据.
BPF_MAP_TYPE_ARRAY和BPF_MAP_TYPE_PERCPU_ARRAY两种类型的 bpf map- 两个实际的例子
定义数据结构, 这个部分在 bpf 程序中
BPF_MAP_TYPE_ARRAY 数组
- 固定长度 预先分配, 32 整数索引, 访问
O(1) - 多个 cpu 共享无碍
struct bpf_object 代表了 elf 文件和其全部内容, map 也在其中. 通过名称找到 map 再找到 map 的 fd
find_map_fd->bpf_map__fd=bpf_object__find_map_fd_by_name
用户空间读取 map:
bpf_map_lookup_elem: 将值存入用户传入指针 (内核也能调用, 两者实现有一些区别)bpf_obj_get_info_by_fd: 查看 map 本身的属性
字节计数器
数据结构定义在 bpf 程序中
elf 文件中不存在 map 中 value 的具体类型只有 value 的长度, 类型安全由 用户 or 内核包装.
xdp 中获取 pkt 的长度 -> struct xdp_md, 这个结构体有点意思: (Note: type __u32 is NOT the real-type)
int xdp_prog_simple(struct xdp_md *ctx)
lock_xadd是 bpf 提供的原子操作函数
用户空间读取不再赘述.
Pre CPU 统计lock_xadd 的时间成本很高, 原子操作 + 内存屏障;
更好的做法是: pre cpu, 每个 cpu 都有一个数据的副本, 读写都是在自己的副本上;
求和的成本就转移到了 userspace;
对于 BPF_MAP_TYPE_PERCPU_ARRAY 调用 bpf_map_lookup_elem 返回的是这个 数据类型 * cpu 数量
03 节中对用户空间读取 bpf map 还有一个隐形限制: 必须是加载 bpf 的程序才能读取到 map.
破局的关键: 可以将 bpf map 挂载到文件系统,公开访问;
base-04 将 03 的程序进一步划分成了:
- xdp_loader.c
- xdp_stats.c: 如果是仅读取 map 甚至不需要 libxdp, libbpf 就足够了.
将 bpf_map pin 到文件系统 -> bpf_object__pin_maps (struct bpf_object *obj, const char *path)
- 与其他 libxdp 的函数一样, 包接包送,一学就会.
- 传入的子路径如何不存在, 这个函数也会直接给你创建;
bpf_map 的挂载流程 -> pin_maps_in_bpf_object
- 多了对程序启动前遗留状态的处理;
- 在子路径已存在文件, 就先
int bpf_object__unpin_maps(struct bpf_object *obj, const char *path)卸载 - 最后挂上 map
原文特别提了一点: xdp_loader.c 的挂载并不会重用已存在的 map
- 说人话就是,一个 map 你通过 xdp_loader.c 挂载两次,已有的 map 就删掉了, 第二次是一个新创建的 map;
- 但是通过 iproute2 命令不会, 第二次挂载时会尝试将 已有的 map 关联到 bpf 程序上, 即使 bpf 程序逻辑变了, 只要 map 定义不变, 已有数据在新 bpf 程序中就能得到保留.
那么如何在 xdp_loader.c 中实现 iproute2 的效果? 教程给出了示例:
这一节内容可能比较适合对 bpf 程序的监控? maybe
尾巴
这一篇其实也出现了拖延症, 但是只看短期目标还是好的, 一个一个子任务无限拆解, 快速反馈.这样才是自己的节奏.
Generated by RSStT. The copyright belongs to the original author.