最基础的 eBPF 示例程序 - Hello World
最基础的 eBPF 示例程序 - Hello World
这是一个最简单的 eBPF 程序,它会在每次execve系统调用时打印一条消息。
一、完整示例代码
1.1 eBPF 内核程序 (hello.bpf.c)
// hello.bpf.c - 最基础的 eBPF Hello World#include<linux/bpf.h>#include<bpf/bpf_helpers.h>// 必须的许可证声明charLICENSE[]SEC("license")="GPL";// 定义一个简单的 tracepoint 程序SEC("tracepoint/syscalls/sys_enter_execve")inthello_world(void*ctx){bpf_printk("Hello, eBPF World! PID: %d\n",bpf_get_current_pid_tgid()>>32);return0;}1.2 用户态加载程序 (hello.c)
// hello.c - 使用 libbpf 加载 eBPF 程序#include<stdio.h>#include<unistd.h>#include<bpf/libbpf.h>#include<bpf/bpf.h>intmain(intargc,char**argv){structbpf_object*obj;structbpf_program*prog;intprog_fd;// 打开 eBPF 对象文件obj=bpf_object__open_file("hello.bpf.o",NULL);if(libbpf_get_error(obj)){fprintf(stderr,"Failed to open BPF object\n");return1;}// 加载 eBPF 程序到内核if(bpf_object__load(obj)){fprintf(stderr,"Failed to load BPF object\n");return1;}// 查找 tracepoint 程序prog=bpf_object__find_program_by_name(obj,"hello_world");if(!prog){fprintf(stderr,"Failed to find BPF program\n");return1;}// 获取程序文件描述符prog_fd=bpf_program__fd(prog);if(prog_fd<0){fprintf(stderr,"Failed to get program FD\n");return1;}// 附加到 tracepointstructbpf_link*link=bpf_program__attach_tracepoint(prog,"syscalls","sys_enter_execve");if(libbpf_get_error(link)){fprintf(stderr,"Failed to attach tracepoint\n");return1;}printf("eBPF program attached! Run some commands and check /sys/kernel/debug/tracing/trace_pipe\n");printf("Press Ctrl+C to exit\n");// 等待用户中断while(1){sleep(1);}// 清理bpf_link__destroy(link);bpf_object__close(obj);return0;}1.3 Makefile
# Makefile CLANG ?= clang BPF_CFLAGS = -target bpf -g -O2 -Wall all: hello # 编译 eBPF 字节码 hello.bpf.o: hello.bpf.c $(CLANG) $(BPF_CFLAGS) -c $< -o $@ # 编译用户态程序 hello: hello.c hello.bpf.o gcc -g -O2 -Wall -o hello hello.c -lbpf -lelf -lz clean: rm -f hello.bpf.o hello run: all sudo ./hello .PHONY: all clean run二、编译和运行
# 1. 编译make# 2. 运行(需要 root 权限)sudo./hello# 3. 在另一个终端执行一些命令lspscat/etc/passwd# 4. 查看 eBPF 输出sudocat/sys/kernel/debug/tracing/trace_pipe三、更简单的版本:使用 bpftrace
如果不想写 C 代码,可以用 bpftrace 实现同样的功能:
# 安装 bpftracesudoaptinstallbpftrace# 一行命令的 Hello Worldsudobpftrace-e'tracepoint:syscalls:sys_enter_execve { printf("Hello from %s (PID %d)\n", comm, pid); }'# 或者保存为脚本文件 hello.btcat>hello.bt<<'EOF' #!/usr/bin/bpftrace tracepoint:syscalls:sys_enter_execve { printf("Hello from %s (PID %d)\n", comm, pid); } EOFchmod+x hello.btsudo./hello.bt四、代码解释
4.1 eBPF 程序部分
SEC("tracepoint/..."):指定程序类型(这里是 tracepoint)bpf_printk():最简单的输出函数,写入/sys/kernel/debug/tracing/trace_pipebpf_get_current_pid_tgid():获取当前进程 PID
4.2 用户态部分
bpf_object__open_file():打开编译好的 eBPF 字节码bpf_object__load():将 eBPF 程序加载到内核bpf_program__attach_tracepoint():挂载到指定 tracepoint
五、预期输出
在运行程序后,执行一些命令,你会看到类似输出:
$sudocat/sys/kernel/debug/tracing/trace_pipe<...>-12345[000]....123456.789012: 0x00000001: Hello, eBPF World!PID:12345bash-12346[001]....123457.890123: 0x00000001: Hello, eBPF World!PID:12346ls-12347[002]....123458.901234: 0x00000001: Hello, eBPF World!PID:12347六、这个示例展示了什么?
| 特性 | 说明 |
|---|---|
| ✅最基本的 eBPF 程序 | 只有几行代码 |
| ✅编译流程 | 从 C 到 BPF 字节码 |
| ✅加载机制 | 用户态程序加载 eBPF |
| ✅挂载点 | 挂载到 tracepoint |
| ✅数据输出 | 使用bpf_printk |
| ✅完整工具链 | 编译、运行、查看结果 |
这就是 eBPF 的 Hello World,它展示了 eBPF 程序的基本结构、编译方法和运行机制。从此出发,可以扩展到更复杂的 kprobe、uprobe、XDP 等程序类型。
