用戶空間所有的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()通常僅用于特殊場景或學習目的。