type
Post
status
Published
slug
2022/12/12/The-BPF-Learning-series/hello-bpf-world
summary
tags
eBPF
BPF
Linux
category
BPF
icon
password
new update day
URL
Property
Jan 14, 2023 06:04 AM
created days
Last edited time
Jan 14, 2023 06:04 AM
0 运行环境配置
运行环境相关的配置、大家可以参考我这一篇文章
1 Hello world 程序分析
1.1 文件结构
整个 Hello world 例程包含以下三个文件,其中 bpf_program.c 是与 bpf 程序相关的文件,loader.c 用来加载上面编写的 bpf 程序, Makefile 用来快速构建。下面我们就把各个文件中的内容展示出来、对各个文件都进行分析了解一下。
. ├── bpf_program.c ├── loader.c └── Makefile
1.2 bpf_program.c
这是一个很简单的 BPF hello world 程序,但是却包含着 BPF 运行所需的必要条件。
#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("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";
SEC 属性
- 我们使用
SEC
属性告知 BPF 虚拟 机何时运行此程序。在本例中,当检测到execve
系统调用跟踪点被执行时, BPF 程序将运行 。
- 跟踪点
- 跟踪点是内核二进制代码中的静态标记,允许开发人员 注入代码来检查内核的执行。这里,只需知道
execve
是执行其他程序的指令。当内核检测到execve
执行时, BPF 程序被执行,我们会看到消息输出Hello,BPF World !
。
- 程序许可证
- 在示例的最后,指定了程序许可证。因为 Linux 内核采用
GPL
许可证,所以它只能加载GPL
许可证的程序。如果将程序设置为其他许可证,内核将拒绝加载该程序。
1.3 loader.c
这个文件是用来将编译完成的 BPF 程序加载至内核中运行的,使用内核提供的
load_bpf_file
辅助函数,它将获取一个二进制文件将它加载到内核中,对编译和加载的程序按模板抽象进行处理。#include "bpf_load.h" #include <stdio.h> int main(int argc, char **argv) { if (load_bpf_file("bpf_program.o") != 0) { printf("The kernel didn't load the BPF program\n"); return -1; } read_trace_pipe(); return 0; }
1.4 Makefile
程序编译脚本,解放双手。
CLANG = clang # 使用 clang 进行编译 EXECABLE = monitor-exec # 生成的可执行文件名字 BPFCODE = bpf_program # bpf 代码名称 BPFTOOLS = /kernel-src/samples/bpf # bpf 内核工具函数 BPFLOADER = $(BPFTOOLS)/bpf_load.c # bpf 加载函数 # 必要的文件包含 CCINCLUDE += -I/kernel-src/tools/testing/selftests/bpf LOADINCLUDE += -I/kernel-src/samples/bpf LOADINCLUDE += -I/kernel-src/tools/lib LOADINCLUDE += -I/kernel-src/tools/perf LOADINCLUDE += -I/kernel-src/tools/include LIBRARY_PATH = -L/usr/local/lib64 BPFSO = -lbpf # Setting -DHAVE_ATTR_TEST=0 for the kernel containing below patch: # 06f84d1989b7 perf tools: Make usage of test_attr__* optional for perf-sys.h # # The patch was included in Linus's tree starting v5.5-rc1, but was also included # in stable kernel branch linux-5.4.y. So it's hard to determine whether a kernel # is affected based on the kernel version alone: # - for a v5.4 kernel from Linus's tree, no; # - for a v5.4 kernel from the stable tree (used by many distros), yes. # # So let's look at the actual kernel source code to decide. # # See more context at: # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=06f84d1989b7e58d56fa2e448664585749d41221 # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fce9501aec6bdda45ef3a5e365a5e0de7de7fe2d CFLAGS += $(shell grep -q "define HAVE_ATTR_TEST 1" /kernel-src/tools/perf/perf-sys.h \ && echo "-DHAVE_ATTR_TEST=0") .PHONY: clean $(CLANG) bpfload build # .PHONY 表示 clean 是个伪目标文件。 clean: rm -f *.o *.so $(EXECABLE) build: ${BPFCODE.c} ${BPFLOADER} # clang -O2 -target bpf -c bpf_program.c -I/kernel-src/tools/testing/selftests/bpf -o bpf_program.o $(CLANG) -O2 -target bpf -c $(BPFCODE:=.c) $(CCINCLUDE) -o ${BPFCODE:=.o} bpfload: build # clang -o monitor-exec -lelf -I/kernel-src/samples/bpf \ -I/kernel-src/tools/lib -I/kernel-src/tools/perf \ -I/kernel-src/tools/include -L/usr/local/lib64 -lbpf \ /kernel-src/samples/bpf/bpf_load.c loader.c clang $(CFLAGS) -o $(EXECABLE) -lelf $(LOADINCLUDE) $(LIBRARY_PATH) $(BPFSO) \ $(BPFLOADER) loader.c $(EXECABLE): bpfload .DEFAULT_GOAL := $(EXECABLE)
如果上面的 Makefile 看不明白的话、可以看看下面的这个 Makefile 文档。
2 运行测试
好的、上面我们已经把 Hello world 例程里面涉及到的所有代码已经分析完毕,接下来我们就应该开始实际的测试,看一下这个代码最终运行起来的效果如何。
2.1 编译代码
- 清理工作空间
make clean ls bpf_program.c loader.c Makefile README.md
- 运行编译命令
可以看到编译完成的时候、生成了 bpf_program.o 与 monitor-exec 这两个文件、其中 .o 文件就是编译后的 BPF 二进制文件,而 monitor-exec 则是 BPF 代码加载程序。
make bpfload ls bpf_program.c bpf_program.o loader.c Makefile monitor-exec README.md
2.2 运行测试
执行
sudo ./monitor-exec
命令将 BPF 程序加载至内核中去,这个时候再开启一个终端执行其它命令、查看程序的工作状态。如果你不使用 sudo 运行该程序,将会返回错误信息,因为对于大多数 BPF 程序而言,只能由 root 特权用户加载到内核中。
可以看到每次我们运行一个命令、程序都会给我们打印一条语句与期望中的程序运行状态一致。
程序停止后,消息将不在终端上显示。 一 旦程序终止,加载的 BPF 程序将从 BPF 虚拟机中卸载。
sudo ./monitor-exec bash-3469 [000] .... 22988.134376: 0: Hello, BPF World! bash-3470 [000] .... 22991.667826: 0: Hello, BPF World! sshd-3471 [000] .... 23012.457993: 0: Hello, BPF World! sshd-3473 [000] .... 23012.505438: 0: Hello, BPF World! sshd-3475 [000] .... 23012.542841: 0: Hello, BPF World! bash-3476 [000] .... 23012.544745: 0: Hello, BPF World! bash-3478 [000] .... 23012.546810: 0: Hello, BPF World! bash-3479 [000] .... 23012.547796: 0: Hello, BPF World! bash-3480 [000] .... 23012.549280: 0: Hello, BPF World! bash-3481 [000] .... 23012.550439: 0: Hello, BPF World! grepconf.sh-3482 [000] .... 23012.551521: 0: Hello, BPF World! bash-3484 [000] .... 23012.552688: 0: Hello, BPF World! bash-3485 [000] .... 23012.553189: 0: Hello, BPF World! bash-3487 [000] .... 23012.553985: 0: Hello, BPF World! bash-3488 [000] .... 23012.554734: 0: Hello, BPF World! bash-3489 [000] .... 23012.555457: 0: Hello, BPF World! grepconf.sh-3490 [000] .... 23012.556176: 0: Hello, BPF World! bash-3491 [000] .... 23012.557051: 0: Hello, BPF World! grepconf.sh-3492 [000] .... 23012.557723: 0: Hello, BPF World! bash-3494 [000] .... 23012.558787: 0: Hello, BPF World! bash-3495 [000] .... 23012.559489: 0: Hello, BPF World! bash-3497 [000] .... 23012.560923: 0: Hello, BPF World!
3 源代码
欢迎加入“喵星计算机技术研究院”,原创技术文章第一时间推送。

- 作者:tangcuyu
- 链接:https://expoli.tech/articles/2022/12/12/The-BPF-Learning-series/hello-bpf-world
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章