https://zhuanlan.zhihu.com/p/480811707
eBPF(extended Berkeley Packet Filter)
可谓 Linux 社区的新宠,很多大公司都开始投身于 eBPF
技术,如 Goole、Facebook、Twitter 等。
乐高积木
的话就会深有体会,乐高积木就是通过不断向主体添加积木来组合出更庞大的模型。
扩展的伯克利包过滤器
。一般来说,要向内核添加新功能,需要修改内核源代码或者编写 内核模块
来实现。而 eBPF 允许程序在不修改内核源代码,或添加额外的内核模块情况下运行。
bpf()
系统调用把 eBPF 字节码加载到内核。bpf()
系统调用把 eBPF 字节码加载到内核时,内核先会对 eBPF 字节码进行安全验证。JIT(Just In Time)
技术将 eBPF 字节编译成本地机器码(Native Code)。kprobes
的运行路径上)。当内核运行到这些路径时,就会触发执行相应路径上的 eBPF 机器码。如果大家使用过 Java 编写程序的话,会发现 eBPF 与 Java 的AOP(Aspect Oriented Programming 面向切面编程)概念很像。
切点
和 拦截器
。切点
:程序中某个具体的业务点(方法)。拦截器
:拦截器其实是一段 Java 代码,用于拦截切点在执行前(或执行后),先运行这段 Java 代码。
fork()
系统调用的运行情况。注意:由于 eBPF 对内核的版本有较高的要求,不同版本的内核对 eBPF 的支持可能有所不相同。所以使用 eBPF 时,最好使用最新版本的内核。
本文使用Ubuntu 20.20
(内核版本为5.8.1)作为解说。
$ sudo apt-get install bpfcc-tools linux-headers-$(uname -r)
bcc -v
来测试是否安装成功。如果安装失败,可以参考官网安装文档,如下:https://github.com/iovisor/bcc/blob/master/INSTALL.md
hello world
程序,所以我们也以编写 hello world
程序作为第一步吧。使用 C 编写 eBPF 程序
int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}
使用 Python 和 BCC 工具开发一个用户态程序
#!/usr/bin/env python3
# 1) 加载 BCC 库
from bcc import BPF
# 2) 加载 eBPF 内核态程序
b = BPF(src_file="hello.c")
# 3) 将 eBPF 程序挂载到 kprobe
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) 读取并且打印 eBPF 内核态程序输出的数据
b.trace_print()
do_sys_openat2()
是系统调用 openat()
在内核中的实现。/sys/kernel/debug/tracing/trace_pipe
的内容(bpf_trace_printk()
函数会将信息写入到此文件),并打印到标准输出中。运行 eBPF 程序
root
用户来运行:$ sudo python3 hello.py
$ sudo python3 hello.py
b' python3-31683 [001] .... 614653.225903: 0: Hello, World!'
b' python3-31683 [001] .... 614653.226093: 0: Hello, World!'
b' python3-31683 [001] .... 614653.226606: 0: Hello, World!'
b' <...>-31684 [000] .... 614654.387288: 0: Hello, World!'
b' irqbalance-669 [000] .... 614658.232433: 0: Hello, World!'
...