eBPF(extended Berkeley Packet Filter, 可扩展伯克利包过滤器) 对内核进行动态编程,以实现高效联网、可观察性、可追踪性和安全性
介绍
eBPF 是从 BPF(也称 cBPF:classic Berkeley Packet Filter) 发展而来的,BPF 专门用来过滤网络数据包
eBPF 允许程序在不修改内核源代码,或添加额外的内核模块情况下运行
- 特征
无侵入特性:观测成本极低,应用无需修改任何代码和重启进程
动态可编程性:无需重启探针,动态下发 eBPF 脚本即可修改探针侧的逻辑
高性能:自带 JIT 编译,使探针能够获得内核本地运行的效率
安全:verifier 机制限制了 eBPF 脚本能够访问的内核函数,保证内核运行的稳定
eBPF 架构
转自
- 用户态
- 用户编写
eBPF 程序,可以使用 eBPF 汇编或者 eBPF 特有的 C 语言来编写
- 使用
LLVM/CLang 编译器,将 eBPF 程序编译成 eBPF 字节码
- 调用
bpf() 系统调用把 eBPF 字节码加载到内核
- 内核态
- 当用户调用
bpf() 系统调用把 eBPF 字节码加载到内核时,内核先会对 eBPF 字节码进行安全验证
- 使用
JIT(Just In Time) 技术将 eBPF 字节编译成本地机器码(Native Code)
- 根据
eBPF 程序的功能,将 eBPF 机器码挂载到内核的不同运行路径上
- 如用于跟踪内核运行状态的
eBPF 程序将会挂载在 kprobes 的运行路径上
- 当内核运行到这些路径时,会触发执行相应路径上的
eBPF 机器码
上图转载自
eBPF 挂载点
bpftrace_probes 参考
相关链接
持续剖析
持续剖析(Continuous Profiling) 是通过动态实时采集应用程序 CPU、内存等资源申请的堆栈信息,帮助检测和定位应用程序的性能瓶颈,实现包括
- eBPF
- jstack
火焰图(Flame Graph)
- 分析 CPU 占用、内存分配、内存泄漏、墙中热点、网络 IO、文件 IO、锁竞争、异常
- 实现包括:广义火焰图、冰柱状火焰图
eBPF 使用
BCC 工具
BCC 工具支持使用 Python 和 C 语言组合来编写 eBPF 程序,参考
# ubuntu
sudo apt-get install bcc bpfcc-tools linux-headers-$(uname -r)
$ bcc -v
bcc: version 0.16.17
Usage: bcc [-ansi] [-options] [-o output] file [files].
hello world
C 语言编写 eBPF 内核态程序:hello.c
int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}
Python 开发 用户态 程序:hello.py
#!/usr/bin/env python3
from bcc import BPF
# 加载 eBPF 内核态程序
b = BPF(src_file="hello.c")
# 将 eBPF 程序挂载到内核探针 kprobe
# do_sys_openat2() 是系统调用 openat() 在内核中的实现
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 读取并且打印 eBPF 内核态程序输出的数据
b.trace_print()
$ sudo python3 hello.py
b' <...>-2111 [000] ...21 239.916342: bpf_trace_printk: Hello, World!'
b' irqbalance-962 [001] ...21 242.346380: bpf_trace_printk: Hello, World!'
b' irqbalance-962 [001] ...21 242.346886: bpf_trace_printk: Hello, World!'
...
bpf_trace_printk() 会将信息写入 /sys/kernel/debug/tracing/trace_pipe 中
相关教程