部署BCC环境
以下为Ubuntu系统下的安装命令,其余Linux发行版的安装方法可参考官方教程:Installing BCC。
|
|
- Clang:C语言编译器的前端,负责把eBPF程序编译成eBPF字节码格式;
- llvm:编译器的后端工具链,负责将中间字节码优化并生成最终可执行的机器码。即配合Clang完成整个编译过程,确保生成高效且兼容的 eBPF 字节码;
- bpfcc-tools:预编译好的BCC命令行工具集合(如execsnoop、opensnoop、tcpconnect 等),可以方便快速地使用eBPF功能进行系统和网络监控,无需自己写程序;
- libbpfcc:BCC的核心运行时库;
- libbpfcc-dev:BCC的开发头文件和静态库文件;
- linux-headers-$(uname -r):当前内核版本的内核头文件,编译eBPF程序时需要内核结构和函数的定义,确保编译出的程序与运行的内核版本兼容。
Hello World
eBPF程序:hello.c
|
|
当内核触发某个hook点时,会调用该eBPF程序。内核会把当前事件的上下文(即调用时的寄存器快照)作为参数传递ctx给hello_world函数。
bpf_trace_printk()是一个BPF提供的Helper调用函数,它的作用是将一段字符串输出到内核的tracing子系统,输出在内核调试文件中,可以直接使用cat命令来查看/sys/kernel/debug/tracing/trace_pipe文件。注意该函数性能开销大,专门用于调试,不能高频调用。
用户态程序:hello.py
|
|
- 第1)处:导入了BCC库的BPF模块;
- 第2)处:调用BPF()加载编写的BPF源代码hello.c,BCC会自动使用Clang/LLVM/编译eBPF程序为字节码,并将字节码加载到内核;
- 第3)处:给内核函数do_sys_openat2()安装一个内核探针(即kprobe),并将BPF程序挂载到该内核探针,因此内核调用do_sys_openat2()函数时,会触发执行eBPF程序里的hello_world函数:
- event值:用户态调用open()、openat()等函数后,最终会在内核里调用do_sys_openat2()函数,是打开文件的关键函数;
- fn_name值:事件触发后,调用的函数;
- 第4)处:实时读取内核调试文件/sys/kernel/debug/tracing/trace_pipe中的内容,并打印到终端。
运行eBPF程序
需使用特权运行eBPF程序:
|
|
程序输出结果(截取部分):
- python-9441表示触发eBPF程序的进程名和进程ID;
- [000]表示CPU编号;
- 49441.311742表示系统启动以来的时间戳,49441是秒、311742是微秒;
- bpf_trace_printk: Hello, World!表示实际的调试输出,bpf_trace_printk()函数打印的Hello, World!。