一.libbpf
libbpf是linux内核源码的一部分,位于内核源码的tools/lib/bpf目录,libbpf在github中的repo与内核代码保持一致。
ebpf程序分为内核程序和用户程序:
- 内核程序使用c或者rust编写,通过clang+llvm编译为字节码,然后在内核中被JIT解析为机器码执行,执行效率高。
- 用户程序使用c、golang、python等语言编写,它负责将ebpf内核程序加载到内核(使用bpf系统调用),还负责通过map与bpf程序进行数据交互。
二.demo
以最简单的libbpf开发为例:
内核程序bpf.c
- 负责声明监听点,定义触发时的执行函数;
- 首先,通过clang编译为bpf字节码;
- 然后,通过bpftool将bpf.o生成skeleton类型的.h文件,便于bpf的分发和用户程序对其进行加载;
用户程序load.c:
- 负责#include skeleton.h文件,将bpf加载到内核;
- 内核的bpf虚拟机执行bpf程序时,遇到监听点,会自动执行对应的函数;
三.内核态程序(bpf.c)
使用libbpf编写ebpf程序bpf.c,在该程序中:
定义监听点:tracepoint/syscalls/sys_enter_execve
- 在执行进程时被触发;
定义处理函数:int bpf_prog()
- 监听点被触发后,输出”hello,BPF world“;
- 由于ebpf运行在内核中,它的输出不是stdout,而是内核调试文件/sys/kernal/debug/tracing/trace_pipe;
bpf.c完整代码:
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
static int (*bpf_trace_printk)(const char *fmt, int fmt_size,
...) = (void *)BPF_FUNC_trace_printk;
// 通过SEC()宏定义的数据结构和函数,会放到特定的ELF段中;
// 这样后续在加载BPF字节码时,就可以从这些段中获取所需的元数据。
SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog(void *ctx) {
char msg[] = "Hello, BPF world!";
bpf_trace_printk(msg, sizeof(msg));
return 0;
}
char _license[] SEC("license") = "GPL";
使用clang编译bpf.c:
clang -O2 -g -target bpf -c bpf.c -o bpf.o
bpf.o是一个ELF格式的、内核可加载的二进制文件,在用户代码中,可以将bpf.o加载进内核。
但是,这种加载方式比较复杂,不利于程序的分发,通常使用bpftool将bpf.o生成skeleton.h文件,然后用户代码#include skeleton.h进行bpf的加载:
bpftool gen skeleton bpf.o > bpf.skel.h
四.用户态程序(load.c)
用户程序load.c负责将bpf加载进内核,然后内核在处理系统调用时,若触发了监听点,则自动执行bpf的处理函数。
另外,还可以在bpf程序中定义map,将内核的操作写到map,然后在用户程序中读取map的内容。
load.c完整代码:
#include <sys/resource.h>
#include “bpf.skel.h"
int main() {
struct bpf *skel = bpf__open();
bpf__load(skel);
bpf__attach(skel);
…
// 打印内核调试文件的内容
system("cat /sys/kernel/debug/tracing/trace_pipe");
...
return 0;
}
使用clang编译load.c:
clang load.c -lelf -lbpf -o load
执行:
# ./load
然后在终端下执行ls命令,由于bpf程序调用bpf_trace_printk()写到内核调试文件中,故可以cat读取到:
# ./load
...
<...>-78931 [001] d...1 91707.196092: bpf_trace_printk: Hello, BPF world!
<...>-78931 [001] d...1 91707.197483: bpf_trace_printk: Hello, BPF world!
<...>-78932 [003] d...1 91707.199038: bpf_trace_printk: Hello, BPF world!
参考:
1.https://github.com/libbpf/libbpf
2.https://cheneytianx.github.io/posts/2022/07/
3.https://ebpf.top/post/map_internal/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。