lyft|入门即享受!coolbpf 硬核提升 BPF 开发效率( 二 )


下图是内核模块和 BPF 的对比(图源:https://zhuanlan.zhihu.com/p/444454862) 。

2.4 BPF 指令集
BPF(默认指 eBPF 非 cBPF) 程序指令都是 64 位 , 使用了 11 个 64 位寄存器 , 32 位称为半寄存器(subregister)和一个程序计数器(program counter) , 一个大小为 512 字节的 BPF 栈 。 所有的 BPF 指令都有着相同的编码方式 。 eBPF虚拟指令系统属于 RISC , 拥有 11 个虚拟寄存器、r0-r10 , 在实际运行时 , 虚拟机会把这 11 个寄存器一 一对应于硬件 CPU 的物理寄存器 。 下图是新老指令的对比:

BPF 指令的核心结构体如下 , 每一条 eBPF 指令都以一个 bpf_insn 来表示 , 在 cBPF 中是其他的一个结构体(struct sock_filter ) , 不过最终都会转换成统一的格式 , 这里我们只研究 eBPF:

由结构体中的__u8 code 可以知道 , 一条 BPF 指令是 8 个字节长 。 这 8 位的 code , 第 0、1、2 位表示的是该操作指令的类别 , 共 8 种:

从最低位到最高位分别是:
8 位的 opcode;有 BPF_X 类型的基于寄存器的指令 , 也有 BPF_K 类型的基于立即数的指令 4 位的目标寄存器 (dst) 4 位的原始寄存器 (src) 16 位的偏移(有符号) , 是相对于栈、映射值(map values)、数据包(packet data)等的相对偏移量 32 位的立即数 (imm)(有符号)
8 bit 的 opcode 进一步拆开 , 下图表示的是存储和加载类指令:

下图表示的是运算和跳转指令:

总之 , BPF 指令很简洁 , 我们未必会在开发过程中使用它来进行代码编写(类似于纯汇编 , 会让人崩溃) , 了解这些是有助于我们更深刻的理解 BPF 的运行原理 。
2.5 BPF 的 prog type 和 map
PROG TYPE
BPF 相关的程序 , 首先需要设置为相对应的的程序类型 , 截止 Linux 内核 5.8 程序类型定义有 29 个 , 而且还在持续增加中 , BPF 程序类型(prog_type)决定了程序可以调用的内核辅助函数的子集 , 也决定了程序输入上下文 -- bpf_context 结构的格式 。
我们经常使用BPF程序类型主要涉及以下两类:
跟踪 大部BPF程序都是这一类 , 主要通过kprobe、tracepoint(rawtracepoint)等追踪系统行为及获取系统硬件信息 。 也可以访问特定程序的内存区域 , 从运行进程中提取执行跟踪信息 。
网络 这类BPF程序用于检测和控制系统的网络流量 。 可以对网络接口数据包进行过滤 , 甚至可以完全拒绝数据包 。
用户态是通过系统调用来加载BPF程序到内核的 , 在加载程序时 , 需要传递的参数中有一个字段叫prog_type , 这个就是BPF的程序类型 , 跟踪相关的是:BPF_PROG_TYPE_KPROBE和BPF_PROG_TYPE_TRACEPOINT , 网络相关是:BPF_PROG_TYPE_SK_SKB、BPF_PROG_TYPE_SOCK_OPS等 。 下面是描述BPF程序类型的枚举结构:
enum bpf_prog_type { BPF_PROG_TYPE_UNSPEC /* Reserve 0 as invalid program type */ BPF_PROG_TYPE_SOCKET_FILTER BPF_PROG_TYPE_KPROBE BPF_PROG_TYPE_SCHED_CLS BPF_PROG_TYPE_SCHED_ACT BPF_PROG_TYPE_TRACEPOINT BPF_PROG_TYPE_XDP BPF_PROG_TYPE_PERF_EVENT BPF_PROG_TYPE_CGROUP_SKB BPF_PROG_TYPE_CGROUP_SOCK BPF_PROG_TYPE_LWT_IN BPF_PROG_TYPE_LWT_OUT BPF_PROG_TYPE_LWT_XMIT BPF_PROG_TYPE_SOCK_OPS BPF_PROG_TYPE_SK_SKB BPF_PROG_TYPE_CGROUP_DEVICE BPF_PROG_TYPE_SK_MSG BPF_PROG_TYPE_RAW_TRACEPOINT BPF_PROG_TYPE_CGROUP_SOCK_ADDR BPF_PROG_TYPE_LWT_SEG6LOCAL BPF_PROG_TYPE_LIRC_MODE2 BPF_PROG_TYPE_SK_REUSEPORT BPF_PROG_TYPE_FLOW_DISSECTOR /* See /usr/include/linux/bpf.h for the full list. */; BPF MAP
BPF 的 map 可用于内核 BPF 程序和用户应用程序之间实现双向的数据交换 ,是重要基础数据结构 , 它可以通过声明 struct bpf_map_def 结构完成创建 。
关于 BPF 最吸引人的一个方面 , 就是运行在内核上的程序可以在运行时使用消息传递相互通信 , 而 BPF Map 就是用户空间和内核空间之间的数据交换、信息传递的桥梁 。