用户空间所有的BPF相关函数(如libbpf库提供的接口)最终都会通过bpf()系统调用与内核交互
下面是一个简单的例子,展示如何直接使用bpf()系统调用加ZAI一个BPF程序,以及如何用libbpf封装后的函数实现相同功能:
1. 直接使用 bpf() 系统调用的例子
#include <linux/bpf.h> #include <sys/syscall.h> #include <unistd.h> #include <stdio.h> #include <errno.h>
// 封装bpf系统调用(实际使用时建议用libbpf) int bpf_syscall(enum bpf_cmd cmd, union bpf_attr *attr) { return syscall(__NR_bpf, cmd, attr, sizeof(*attr)); }
int main() { union bpf_attr attr = {}; // 示例:通过BPF_PROG_LOAD命令加ZAI一个空程序(实际需填充字节码) attr.prog_type = BPF_PROG_TYPE_KPROBE; // 程序类型 attr.insns = (__u64)NULL; // 指令数组(实际需填充有效指令) attr.insn_cnt = 0; // 指令数量(实际需>0) attr.license = (__u64)"GPL"; // 许可证 int fd = bpf_syscall(BPF_PROG_LOAD, &attr); if (fd < 0) { perror("BPF_PROG_LOAD failed"); return 1; } printf("BPF程序加ZAI成功,fd=%d\n", fd); return 0; }
|
bpf()系统调用通过union bpf_attr传递参数,根据不同的cmd(如BPF_PROG_LOAD)解释该结构体的字段。
实际使用时需要填充有效的BPF指令(如从.o文件读取),这里简化了流程。
使用 libbpf 封装的等价例子
#include <bpf/bpf.h> #include <bpf/libbpf.h> #include <stdio.h>
int main() { struct bpf_object *obj; struct bpf_program *prog; // 1. 打开BPF对象文件(libbpf会解析并调用bpf()系统调用) obj = bpf_object__open_file("example.o", NULL); if (!obj) { fprintf(stderr, "无法打开BPF对象文件\n"); return 1; } // 2. 加ZAI BPF程序到内核(内部调用BPF_PROG_LOAD) if (bpf_object__load(obj)) { fprintf(stderr, "加ZAI BPF程序失败\n"); return 1; } // 3. 获取程序fd(libbpf已通过bpf()获取) prog = bpf_object__find_program_by_name(obj, "my_prog"); int fd = bpf_program__fd(prog); printf("BPF程序加ZAI成功,fd=%d\n", fd); bpf_object__close(obj); return 0; }
|
libbpf封装了bpf()系统调用的细节:
1. bpf_object__open_file解析ELF文件,提取BPF程序和映射。
2. bpf_object__load内部调用bpf(BPF_PROG_LOAD)加ZAI程序。
3. bpf_program__fd返回程序的文件描述符(由内核通过bpf()分配)。
关键对比
操作 |
直接调用bpf() |
使用libbpf |
加ZAI BPF程序 |
手动填充union bpf_attr ,调用BPF_PROG_LOAD |
bpf_object__load() 自动处理 |
指令传递 |
需手动构造指令数组 |
从.o 文件自动解析 |
映射创建 |
调用BPF_MAP_CREATE |
bpf_map__fd() 封装 |
错误处理 |
需手动检查errno |
提供更友好的错误信息 |
总结
底层:所有BPF操作最终通过bpf()系统调用完成。
上层:libbpf等库封装了复杂性(如ELF解析、指令修正、映射管理),推荐优先使用。直接调用bpf()通常仅用于特殊场景或学习目的。