BPF学习笔记(三)

您所在的位置:网站首页 BTF文件 BPF学习笔记(三)

BPF学习笔记(三)

2024-06-02 04:45| 来源: 网络整理| 查看: 265

        上一篇文章中BPF的例子是以BCC的整体框架为基础,本篇介绍一下基于libbpf库函数为基础,结合内核中的bpf的sample为基础编写一个BPF的程序,本篇介绍是以《linux-observability-with-bpf》这本书第二章的例子为基础,由于内核版本的不同,本篇介绍是以Linux5.16内核为基础,Linux5.16内核中的接口函数与书中的给到的程序案例有较大的差别。 1. 下载并编译内核 1) 确定和编译内核版本 下载需要编译的内核版本,本次使用的内核版本为:Linux-5.16.11版本。 2) 修改内核的配置文件,设置CONFIG_DEBUG_INFO_BTF=y,编译调试, 3) 编译内核 make olddefconfig make -j 4 make modules_install make install 通过命令grub2-set-default 设置启动的内核 4) 重启机器使用安装的新内核版本:5.16.11.frank+ 5) 确定/sys/kernel/btf/vmlinux文件是否存在。 2. 编译安装libbpf库 1) 进入目录tools/lib/bpf  在该目录下执行make install

2) 修改/etc/ld.so.conf 文件,添加/usr/local/lib64  执行ldconfig,查看ldconfig  -v 2> /dev/null | grep libbpf 如果没有编译libbpf库,在编译bpf程序中会出现,下面的错误信息

        上述准备工作完毕后,有以下两种方式编译bpf的例子,第一种方式,把编写的bpf程序放到sample/bpf目录下,首先编译sample/bpf, 1. 编译内核下samples/bpf目录下的bpf 1) 在编译之前安装必要的工具: yum -y install binutils-devel yum -y install readline-devel yum -y install  dwarves  libdwarves1  libdwarves1-devel(dwarves版本号最好大于1.17) yum -y install libcap-devel   2) 在sample/bpf目录下  make  在编译的过程中,确定vmlinux的位置, make VMLINUX_BTF=/sys/kernel/btf/vmlinux -C samples/bpf 使用vmlinux产生vmlinux.h头文件,CO:RE开发需要vmlinux.h文件,(Compile once, run everywhere) bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 2. 编译自己编写的bpf程序 1)编译通过完成后,修改sample/bpf的目录下的Makefile文件,添加下面的三行代码: hello-objs := hello_user.o always-y += hello_kern.o tprogs-y += hello hello_user 为我们用户空间的程序名,hello_kern为我们的内核空间程序名。 2)Kernel hello_kern.c程序:

#include #include #include #include #include "trace_common.h" SEC("tracepoint/syscalls/sys_enter_execve") int bpf_prog(struct pt_regs *ctx) { char msg[] = "Hello, BPF World!"; bpf_trace_printk(msg, sizeof(msg)); return 0; } char _license[] SEC("license") = "GPL"; u32 _version SEC("version") = LINUX_VERSION_CODE;

kernel程序比较简单,意思是在执行到内核中的execve函数时,打印 Hello BPF World!

3) 应用程序 hello_user.c

#include #include #include #include #define DEBUGFS "/sys/kernel/debug/tracing/" int load_bpf_file(char *filename); int load_bpf_file(char *path) { struct bpf_object *obj; struct bpf_program *prog; struct bpf_link *link = NULL; int progs_fd; printf("%s\n",path); obj = bpf_object__open_file(path, NULL); if (libbpf_get_error(obj)) { fprintf(stderr, "ERROR: opening BPF object file failed\n"); return 0; } if (bpf_object__load(obj)) { fprintf(stderr, "ERROR: loading BPF object file failed\n"); goto cleanup; } prog = bpf_object__find_program_by_name(obj, "bpf_prog"); if (!prog) { printf("finding a prog in obj file failed\n"); goto cleanup; } link = bpf_program__attach(prog); if (libbpf_get_error(link)) { fprintf(stderr, "ERROR: bpf_program__attach failed\n"); link = NULL; goto cleanup; } read_trace_pipe(); cleanup: bpf_link__destroy(link); bpf_object__close(obj); return 0; } void read_trace_pipe(void) { int trace_fd; trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); if (trace_fd < 0) return; while (1) { static char buf[4096]; ssize_t sz; sz = read(trace_fd, buf, sizeof(buf) - 1); if (sz > 0) { buf[sz] = 0; puts(buf); } } } int main(int argc, char **argv) { if (load_bpf_file("hello_kern.o") != 0) { printf("The kernel didn't load the BPF program\n"); return -1; } }

执行上面的程序输出如下结果: 第二种方法:  如果不把编写的bpf示例程序放到,samples/bpf目录下,可以单独写一个makefile文件,内容如下:

CLANG = clang EXECABLE = monitor-exec BPFCODE = bpf_program BPFTOOLS = /data/kernel/v1/linux-stable/samples/bpf CCINCLUDE += -I/data/kernel/v1/linux-stable/tools/testing/selftests/bpf LOADINCLUDE += -I/data/kernel/v1/linux-stable/samples/bpf LOADINCLUDE += -I/data/kernel/v1/linux-stable//tools/lib LOADINCLUDE += -I/data/kernel/v1/linux-stable/tools/perf LOADINCLUDE += -I/data/kernel/v1/linux-stable/tools/include LIBRARY_PATH = -L/usr/local/lib64 BPFSO = -lbpf CFLAGS += $(shell grep -q "define HAVE_ATTR_TEST 1" /data/kernel/v1/linux-stable/tools/perf/perf-sys.h \ && echo "-DHAVE_ATTR_TEST=0") .PHONY: clean $(CLANG) bpfload build clean: rm -f *.o *.so $(EXECABLE) build: ${BPFCODE.c} $(CLANG) -O2 -target bpf -c $(BPFCODE:=.c) $(CCINCLUDE) -o ${BPFCODE:=.o} bpfload: build clang $(CFLAGS) -o $(EXECABLE) -lelf $(LOADINCLUDE) $(LIBRARY_PATH) $(BPFSO) \ loader.c $(EXECABLE): bpfload .DEFAULT_GOAL := $(EXECABLE)

 1)本程序虽然以《linux-observability-with-bpf》第2章的程序为基础,但是随着内核的更新,采用5.16版本内核时load_bpf_file函数已经被移除了,需要重新调用函数实现load_bpf_file函数。 2)随着bpf和内核版本的不断变化,参考本文时需要重点关注不同的内核版本、bpftool、gcc等各类工具的版本。 3) 内核源代码中的samples/bpf目录下有大量的bpf的示例程序可以参考。 4)使用bcc框架版本的bpf程序和使用libbpf库bpf程序在编写方式上会有所不同,注意不同的接口函数。 5)centos安装libbpf -devel

sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-*

sed -i -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-*

dnf --enablerepo=PowerTools install libbpf-devel

参考文献:https://blog.aquasec.com/vmlinux.h-ebpf-programs



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3