eBPF是什么
eBPF全称是extended Berkeley Packet Filter,是Linux内核上的一个强大的网络和性能分析工具,它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
eBPF程序主要由两部分构成:内核态部分和用户态部分。内核态部分包含eBPF程序的实际逻辑,用户态部分负责加载、运行和监控内核态程序。通常内核态可以由C或Rust编写,用户态可以用Python、Golang、C等等编写,用户态部分负责加载、运行和监控内核态程序。
在linux内核中有一套hook函数机制,hook机制的作用是挂载一种未确定的逻辑处理过程以实现对特定位置(hook点)的处理。eBPF程序加载到内核后通过事件驱动,当内核或应用程序通过某个hook点时触发执行。预定义的钩子包括:系统调用、函数进入/退出、内核跟踪点、网络事件等。
eBPF开发环境准备
|
|
LLVM
和Clang
是编译eBPF程序所需要的依赖环境。
|
|
解决fatal error: 'asm/types.h' file not found
问题,
|
|
ecc
将c语言源代码编译成eBPF字节码
|
|
ecli
可以帮助我们将eBPF字节码加载到内核运行
第一个eBPF的HelloWorld程序
创建一个helloworld.c
文件,内容如下:
|
|
- include引入必要的头文件
LICENSE
变量定义了许可证信息,暂且可以当作一个固定写法SEC("tracepoint/sched/sched_process_fork")
SEC
是一个宏定义,它里面的值说明这是一个tracepoint
类型的程序,插入如函数为sched_process_fork
,这是一个内核中的函数,系统创建进程时 最终会调用该函数- 定义了
tr_hello
函数,void* ctx
ctx本来是具体类型的参数, 但是由于我们这里没有使用这个参数,因此就将其写成void *类型 bpf_get_current_pid_tgid()
是BPF中的一个辅助函数,用户获取当前进程IDbpf_trace_printk()
是一种将信息输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe)简单机制
这个代码的逻辑就是当系统创建一个进程时就打印Hello World from PID:**
用ecc工具编译为eBPF字节码,如下:
|
|
ls
命令查看:
|
|
编译成功后用ecli
工具运行,如下:
|
|
打开另一个终端执行cat /sys/kernel/debug/tracing/trace_pipe
,
再打开一个终端执行ls
或ps
等命令,这些命令会创建进程,此时查看cat /sys/kernel/debug/tracing/trace_pipe
的终端窗口有如下结果
|
|
每创建一个进程,都会打印一次对应的记录,ctrl
+C
结束运行的程序,自此我们运行了我们的第一个eBPF程序
eBPF程序的类型
Tracepoint
: Tracepoint程序跟踪内核中的事件和函数调用,用于进行静态插桩。静态插桩是指内核中已经预先定义了许多hook点,eBPF字节码加载进内核后会在对应位置执行。Kprobes
:Kprobes程序与Tracepoint程序相反,用作动态插桩,可以跟踪内核中绝大部分函数(部分禁止访问),甚至在某个指令前后插入我们的代码。
Kprobes包括三种探测手段分别时kprobe、jprobe和kretprobe。kprobe是最基本的探测方式,是实现后两种的基础,它可以在任意的位置放置探测点(就连函数内部的某条指令处也可以)。jprobe基于kprobe实现,它用于获取被探测函数的入参值 kretprobe不仅用于获取被探测函数的返回值,还能够修改函数的返回值。
Kprobes动态插桩可以将我们的代码插入任意位置运行,缺点是不稳定,不同版本的内核代码指令位置不同,效率也比Tracepoint低,因为Tracepoint的预定义hook点是精心设计的,不会对系统性能产生较大影响
如果开发者对内核不熟悉,将Kprobes程序插入到某些执行频率极高的位置,可能会明显影响性能
-
fentry
和fexit
:用于在 Linux 内核函数的入口和退出处进行跟踪。与kprobes相比,fentry和fexit程序有更高的性能和可用性,fexit程序可以访问函数的输入参数和返回值,而kretprobe只能访问返回值。从5.5内核开始,fentry和fexit对eBPF程序可用。 -
uprobe
:uprobe是一种用户空间探针,uprobe探针允许在用户空间程序中动态插桩,插桩位置包括:函数入口、特定偏移处,以及函数返回处。
uprobe基于文件,当一个二进制文件中的一个函数被跟踪时,所有使用到这个文件的进程都会被插桩,包括那些尚未启动的进程,这样就可以在全系统范围内跟踪系统调用。
xdp
:全称eXpress Data Path,是基于eBPF实现的高性能、可编程的数据平面技术。
XDP需要网卡的支持,位于网卡驱动层,当数据包经过DMA存放到ring buffer之后,分配skb之前,即可被XDP处理。可用于高速数据包处理,比如缓解DDOS、负载均衡。
eBPF MAP
特殊的数据结构,用于用户态程序和eBPF程序通信