1

一.libbpf

libbpf是linux内核源码的一部分,位于内核源码的tools/lib/bpf目录,libbpf在github中的repo与内核代码保持一致。

ebpf程序分为内核程序和用户程序:

  • 内核程序使用c或者rust编写,通过clang+llvm编译为字节码,然后在内核中被JIT解析为机器码执行,执行效率高。
  • 用户程序使用c、golang、python等语言编写,它负责将ebpf内核程序加载到内核(使用bpf系统调用),还负责通过map与bpf程序进行数据交互。

image.png

二.demo

以最简单的libbpf开发为例:

  • 内核程序bpf.c

    • 负责声明监听点,定义触发时的执行函数;
    • 首先,通过clang编译为bpf字节码;
    • 然后,通过bpftool将bpf.o生成skeleton类型的.h文件,便于bpf的分发和用户程序对其进行加载;
  • 用户程序load.c:

    • 负责#include skeleton.h文件,将bpf加载到内核;
    • 内核的bpf虚拟机执行bpf程序时,遇到监听点,会自动执行对应的函数;

image.png

三.内核态程序(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/


a朋
63 声望38 粉丝