eBPF综合指南
eBPF 综合指南
目录
- 简介
- 历史与演进
- eBPF 工作原理
- eBPF 架构
- eBPF 程序
- eBPF Maps
- 应用场景
- 工具与生态系统
- 安全考虑
- 性能特性
- 快速入门
- 未来发展方向
简介
eBPF(Extended Berkeley Packet Filter) 是一项革命性技术,它允许在 Linux 内核中安全、高效、可编程地执行沙盒程序,而无需开发内核模块或修改内核源代码。
eBPF 使开发者能够动态扩展内核功能,提供前所未有的可观测性、安全性和网络功能,同时通过验证过程确保安全性。
核心特性
- 安全性:程序在执行前经过验证,确保不会导致内核崩溃
- 高效性:通过 JIT(即时编译)编译为本地机器码,实现接近原生的性能
- 可扩展性:可挂载到各种内核子系统,无需修改内核代码
- 可移植性:通过 CO-RE(Compile Once, Run Everywhere)支持在不同内核版本上运行
- 可编程性:使用熟悉的编程模型,支持 C 或 Rust 语言
历史与演进
起源:BPF(1992年)
Berkeley Packet Filter 由 Steven McCanne 和 Van Jacobson 于 1992 年创建,作为 BSD 操作系统中高效的数据包过滤机制。BPF 使用虚拟机在内核空间过滤数据包,然后再将其复制到用户空间,相比之前的方法显著提高了性能。
演进为 eBPF(2014年)
2014年,Alexei Starovoitov 扩展了 BPF,引入了新功能,创建了 eBPF:
- 扩展的寄存器集(从2个扩展到10个64位寄存器)
- 新的数据类型和指令
- 支持 maps(键值存储)
- 能够挂载到除数据包之外的各种内核事件
- JIT 编译支持
重要里程碑
| 年份 | 里程碑 |
|---|---|
| 1992 | 原始 BPF 引入 |
| 1997 | BPF 集成到 Linux 内核 |
| 2011 | Seccomp-BPF 用于安全计算 |
| 2014 | eBPF 合并到 Linux 内核(v3.18) |
| 2017 | XDP(eXpress Data Path)引入 |
| 2019 | CO-RE(Compile Once, Run Everywhere)支持 |
| 2020 | LSM(Linux Security Modules)钩子 |
| 2021 | eBPF 成为 CNCF 孵化项目 |
| 2023 | Windows eBPF 预览版发布 |
eBPF 工作原理
执行流程
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ eBPF 字节 │ │ 验证器 │ │ JIT/VM │
│ 代码 │────▶│ (安全性 │────▶│ 编译器 │
│ │ │ 检查) │ │ │
└─────────────┘ └──────────────┘ └─────────────┘
│ │
│ ▼
│ ┌──────────────┐
│ │ 本地 │
│ │ 执行 │
└─────────────────────────────▶│ 在内核中 │
└──────────────┘
逐步流程
-
程序编译:eBPF 程序使用 C(受限子集)或 Rust 编写,通过 LLVM/Clang 编译为 eBPF 字节码
-
加载:通过
bpf()系统调用将字节码加载到内核 -
验证:内核验证器执行静态分析:
- 确保程序终止(无无限循环)
- 检查越界内存访问
- 验证寄存器使用和类型
- 验证只调用允许的内核函数
-
JIT 编译:验证后的字节码被编译为主机架构的本地机器码
-
挂载:程序被挂载到特定的钩子点(tracepoint、kprobe、socket 等)
-
执行:当钩子被触发时,eBPF 程序在该事件的上下文中执行
eBPF 架构
组件
┌─────────────────────────────────────────────────────────────┐
│ 用户空间 │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ libbpf │ │ Cilium │ │ bcc │ │ bpftrace│ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ bpftool │ │ P4/eBPF │ │ XDP │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
│
bpf() 系统调用
│
┌─────────────────────────────────────────────────────────────┐
│ 内核空间 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 验证器 │ │ JIT 引擎 │ │ Maps │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 钩子点 │ │
│ ├─────────┬──────────┬─────────┬─────────┬─────────────┤ │
│ │ Kprobes │ Tracepts │ Network │ LSM │ Socket │ │
│ └─────────┴──────────┴─────────┴─────────┴─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ eBPF 程序 (JITed) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
钩子点类型
| 钩子类型 | 描述 | 使用场景 |
|---|---|---|
| Kprobes | 动态内核函数入口/返回 | 函数跟踪、性能分析 |
| Uprobes | 用户空间函数跟踪 | 应用程序分析 |
| Tracepoints | 静态内核检测点 | 事件跟踪、调试 |
| Socket Filters | Socket 上的数据包过滤 | 网络监控、防火墙 |
| XDP | eXpress Data Path(最早的数据包处理) | 高性能网络、DDoS 防护 |
| TC(流量控制) | QoS 和数据包操作 | 网络策略、负载均衡 |
| cGroup | 每个 cgroup 的钩子 | 资源监控、安全策略 |
| LSM(Linux 安全模块) | 安全钩子 | 访问控制、运行时安全 |
| Perf Events | 性能监控事件 | CPU 分析、硬件计数器 |
| Socket Operations | Socket 创建、连接等 | 网络可观测性 |
eBPF 程序
程序类型
eBPF 支持多种程序类型,每种都为特定目的而设计:
enum bpf_prog_type {
BPF_PROG_TYPE_SOCKET_FILTER,
BPF_PROG_TYPE_KPROBE,
BPF_PROG_TYPE_SCHED_CLS,
BPF_PROG_TYPE_SCHED_ACT,
BPF_PROG_TYPE_TRACEPOINT,
BPF_PROG_TYPE_XDP,
BPF_PROG_TYPE_PERF_EVENT,
BPF_PROG_TYPE_CGROUP_SKB,
BPF_PROG_TYPE_CGROUP_SOCK,
BPF_PROG_TYPE_LWT_IN,
BPF_PROG_TYPE_LWT_OUT,
BPF_PROG_TYPE_LWT_XMIT,
BPF_PROG_TYPE_SOCK_OPS,
BPF_PROG_TYPE_SK_SKB,
BPF_PROG_TYPE_CGROUP_DEVICE,
BPF_PROG_TYPE_SK_MSG,
BPF_PROG_TYPE_RAW_TRACEPOINT,
BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
BPF_PROG_TYPE_LWT_SEG6LOCAL,
BPF_PROG_TYPE_LIRC_MODE2,
BPF_PROG_TYPE_SK_REUSEPORT,
BPF_PROG_TYPE_FLOW_DISSECTOR,
BPF_PROG_TYPE_CGROUP_SYSCTL,
BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
BPF_PROG_TYPE_CGROUP_SOCKOPT,
BPF_PROG_TYPE_TRACING,
BPF_PROG_TYPE_STRUCT_OPS,
BPF_PROG_TYPE_EXT,
BPF_PROG_TYPE_LSM,
BPF_PROG_TYPE_SK_LOOKUP,
BPF_PROG_TYPE_SYSCALL,
};
示例:简单的 XDP 程序
// xdp_drop.c
#include
#include
SEC("xdp")
int xdp_drop_prog(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
// 丢弃所有数据包
return XDP_DROP;
}
char _license[] SEC("license") = "GPL";
示例:Kprobe 程序
// trace_open.c
#include
#include
#include
SEC("kprobe/do_sys_open")
int trace_do_sys_open(struct pt_regs *ctx)
{
// 记录文件打开事件
bpf_printk("File opened: %s", PT_REGS_PARM1(ctx));
return 0;
}
char _license[] SEC("license") = "GPL";
eBPF Maps
Map 类型
eBPF maps 是键值数据结构,允许在 eBPF 程序之间以及内核和用户空间之间共享数据:
enum bpf_map_type {
BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_ARRAY,
BPF_MAP_TYPE_PERCPU_HASH,
BPF_MAP_TYPE_PERCPU_ARRAY,
BPF_MAP_TYPE_LRU_HASH,
BPF_MAP_TYPE_LRU_PERCPU_HASH,
BPF_MAP_TYPE_LPM_TRIE,
BPF_MAP_TYPE_ARRAY_OF_MAPS,
BPF_MAP_TYPE_HASH_OF_MAPS,
BPF_MAP_TYPE_DEVMAP,
BPF_MAP_TYPE_SOCKMAP,
BPF_MAP_TYPE_CPUMAP,
BPF_MAP_TYPE_XSKMAP,
BPF_MAP_TYPE_SOCKHASH,
BPF_MAP_TYPE_CGROUP_STORAGE,
BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
BPF_MAP_TYPE_QUEUE,
BPF_MAP_TYPE_STACK,
BPF_MAP_TYPE_SK_STORAGE,
BPF_MAP_TYPE_DEVMAP_HASH,
BPF_MAP_TYPE_STRUCT_OPS,
BPF_MAP_TYPE_RINGBUF,
BPF_MAP_TYPE_INODE_STORAGE,
BPF_MAP_TYPE_TASK_STORAGE,
BPF_MAP_TYPE_BLOOM_FILTER,
};
常用 Map 操作
// 创建一个哈希表
struct bpf_map_def SEC("maps") {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(__u64),
.max_entries = 1024,
} my_map;
// eBPF 程序中的 map 操作
__u32 key = 1;
__u64 value = 42;
// 查找
bpf_map_lookup_elem(&my_map, &key);
// 更新
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
// 删除
bpf_map_delete_elem(&my_map, &key);
Map 对比
| Map 类型 | 描述 | 使用场景 |
|---|---|---|
| HASH | 通用哈希表 | 通用查找 |
| ARRAY | 固定大小数组 | 索引数据、计数器 |
| PERCPU_HASH | 每个 CPU 的哈希表 | 无锁统计 |
| LPM_TRIE | 最长前缀匹配树 | IP 路由、CIDR 匹配 |
| RINGBUF | 环形缓冲区 | 高吞吐量事件流 |
| PERF_EVENT_ARRAY | 每个 CPU 的 perf 事件 | 性能监控 |
| SOCKMAP | Socket 映射 | Socket 重定向、连接跟踪 |
应用场景
1. 可观测性和监控
eBPF 提供对系统行为的深度可见性,且开销极小:
- 函数跟踪:跟踪任何内核或用户空间函数
- 性能分析:CPU、内存、I/O 分析,开销极低
- 网络监控:数据包级可见性、连接跟踪
- 应用监控:无需修改代码即可跟踪应用行为
工具:bpftrace、bcc、Bumblebee、Parca
2. 网络
eBPF 彻底改变了网络数据包处理:
- XDP(eXpress Data Path):在最早阶段处理数据包,实现高达 20M+ PPS
- 负载均衡:实现自定义负载均衡算法
- DDoS 防护:以线速过滤恶意流量
- 服务网格:在内核中实现服务网格功能
- 网络策略:实施细粒度的网络安全策略
项目:Cilium、Katran、Cilium Enterprise、Facebook Katran
3. 安全
eBPF 提供强大的安全功能:
- 运行时安全:实时检测和阻止可疑行为
- 文件系统监控:跟踪文件访问和修改
- 进程跟踪:监控进程创建、执行和终止
- 网络安全:实施入侵检测和防护
- 容器安全:保护容器化环境
工具:Falco、Tracee、Tetragon、BPF Observer
4. 性能优化
- TCP 拥塞控制:实现自定义拥塞控制算法
- Socket 优化:优化 socket 缓冲区管理
- Cgroup 感知调度:实现自定义调度策略
- I/O 优化:优化块设备操作
5. 故障排除和调试
- 内核跟踪:跟踪内核事件和函数
- 应用调试:无需重启即可调试应用程序
- 延迟分析:识别延迟瓶颈
- 资源分析:跟踪资源使用模式
工具:bpftrace、bcc、drgn
工具与生态系统
核心库和工具
libbpf
用于加载和与 eBPF 程序交互的官方 C 库。提供:
- eBPF 程序加载
- Map 操作
- CO-RE 支持
- 骨架生成
# 安装 libbpf
sudo apt install libbpf-dev # Ubuntu/Debian
sudo yum install libbpf-devel # RHEL/CentOS
bpftool
用于检查和管理 eBPF 程序和 maps 的官方工具:
# 列出所有 eBPF 程序
sudo bpftool prog list
# 显示程序详细信息
sudo bpftool prog show id 123
# 转储程序指令
sudo bpftool prog dump xlated id 123
# 列出 maps
sudo bpftool map list
# 转储 map 内容
sudo bpftool map dump id 456
bcc(BPF Compiler Collection)
基于 Python 的框架,用于编写带有嵌入式 C 的 eBPF 程序:
from bcc import BPF
# 定义 eBPF 程序
bpf_text = """
#include
BPF_HASH(start, u32);
int do_entry(struct pt_regs *ctx) {
u32 pid = bpf_get_current_pid_tgid();
u64 ts = bpf_ktime_get_ns();
start.update(&pid, &ts);
return 0;
}
"""
# 加载和挂载
b = BPF(text=bpf_text)
b.attach_kprobe(event="do_sys_open", fn_name="do_entry")
bpftrace
用于 eBPF 的高级跟踪语言:
# 按进程统计系统调用
bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'
# 分析 CPU
bpftrace -e 'profile:hz:99 { @[stack] = count(); }'
# 跟踪文件打开
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s
", comm, str(args->filename)); }'
现代框架
Cilium
基于 eBPF 的 Kubernetes 网络、安全和可观测性:
- 使用 eBPF 的网络策略
- 服务网格功能
- 负载均衡
- 可观测性(Hubble)
# 安装 Cilium
cilium install --cluster-name my-cluster
libbpf-bootstrap
使用 libbpf 和 CO-RE 开发 eBPF 应用程序的模板和示例:
git clone https://github.com/libbpf/libbpf-bootstrap
cd libbpf-bootstrap
make
Aya
基于 Rust 的 eBPF 框架,专注于开发者体验:
use aya::{Bpf, programs::KProbe};
#[no_mangle]
pub unsafe extern "C" fn try_to_wake_up(_ctx: *mut c_void) -> i32 {
0
}
// 加载和挂载
let mut bpf = Bpf::load(&[])?;
let program: &mut KProbe = bpf.program_mut("try_to_wake_up")?;
program.load()?;
program.attach("try_to_wake_up", 0)?;
可观测性工具
| 工具 | 语言 | 使用场景 |
|---|---|---|
| bpftrace | bpftrace | 临时跟踪和分析 |
| bcc | Python | 复杂跟踪工具 |
| Bumblebee | Go | Kubernetes 可观测性 |
| Parca | Go | 持续分析 |
| Pixie | C++ | Kubernetes 可观测性 |
| Ebpf-exporter | Go | 从 eBPF 生成 Prometheus 指标 |
安全工具
| 工具 | 描述 |
|---|---|
| Falco | 运行时安全监控 |
| Tracee | Linux 安全可观测性 |
| Tetragon | 安全可观测性和执行 |
| BPF Observer | 安全监控平台 |
安全考虑
验证器
eBPF 验证器是 eBPF 安全的基石。它执行:
- 控制流分析:确保所有路径都终止
- 内存安全:验证所有内存访问
- 类型检查:强制类型安全
- 权限检查:验证只调用允许的辅助函数
- 指针跟踪:跟踪指针来源和边界
安全特性
- 沙盒执行:程序在受限环境中运行
- 无无限循环:保证有界执行
- 无内核内存损坏:只有安全的内存访问
- 权限分离:CAP_BPF 能力用于加载程序
- 命名空间隔离:程序可以感知命名空间
最佳实践
- 使用最新内核:较新的内核具有增强的验证器功能
- 最小化复杂性:更简单的程序更容易验证
- 使用 CO-RE:确保跨内核版本的可移植性
- 遵循辅助函数限制:只使用允许的辅助函数
- 彻底测试:首先在安全环境中验证程序
安全顾虑
- 信息泄露:程序可能泄露内核内存
- 拒绝服务:复杂的程序可能减慢验证速度
- 侧信道:在某些情况下可能存在时序攻击
- 权限提升:谨慎使用 CAP_BPF 权限
性能特性
性能优势
- 接近原生速度:JIT 编译产生高效的本地代码
- 零拷贝:XDP 可以在不复制的情况下处理数据包
- 低开销:内核和用户空间之间的上下文切换最少
- 可扩展:每个 CPU 的 maps 实现无锁操作
性能基准
| 使用场景 | 性能 | 备注 |
|---|---|---|
| XDP Drop | 20-40 MPPS | 以线速丢弃数据包 |
| Socket Filter | 10-20 Gbps | 数据包过滤 |
| Kprobe Tracing | <1% 开销 | 最小的性能影响 |
| Hash Map Lookup | ~50ns | 快速键值操作 |
优化技术
- 使用每个 CPU 的 Maps:消除锁争用
- 批量操作:尽可能使用批量辅助函数
- 提前退出:尽可能提前返回
- 避免循环:首选展开代码或有界循环
- 使用环形缓冲区:高吞吐量事件流
快速入门
前置条件
# 检查内核版本(推荐 4.10+)
uname -r
# 安装所需工具
sudo apt update
sudo apt install -y
clang
llvm
libbpf-dev
linux-headers-$(uname -r)
bpfcc-tools
linux-tools-$(uname -r)
bpftool
Hello World 示例
1. 创建 eBPF 程序(hello.bpf.c)
#include "vmlinux.h"
#include
#include
SEC("tp/syscalls/sys_enter_execve")
int handle_execve(void *ctx)
{
char comm[16];
bpf_get_current_comm(&comm, sizeof(comm));
bpf_printk("Hello, eBPF! Process: %s", comm);
return 0;
}
char LICENSE[] SEC("license") = "GPL";
2. 创建加载器(hello.c)
#include
#include
#include
#include
int main() {
struct bpf_object *obj;
struct bpf_program *prog;
int err;
// 打开 eBPF 对象文件
obj = bpf_object__open_file("hello.bpf.o", NULL);
if (libbpf_get_error(obj)) {
fprintf(stderr, "Failed to open eBPF object
");
return 1;
}
// 加载 eBPF 程序
err = bpf_object__load(obj);
if (err) {
fprintf(stderr, "Failed to load eBPF object
");
return 1;
}
// 查找程序
prog = bpf_object__find_program_by_name(obj, "handle_execve");
if (!prog) {
fprintf(stderr, "Program not found
");
return 1;
}
// 挂载程序
err = bpf_program__attach(prog);
if (err) {
fprintf(stderr, "Failed to attach program
");
return 1;
}
printf("eBPF program loaded! Press Ctrl+C to exit.
");
// 保持程序运行
for (;;) {
sleep(1);
}
return 0;
}
3. 编译和运行
# 编译 eBPF 程序
clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -c hello.bpf.c -o hello.bpf.o
# 编译加载器
clang -g -O2 hello.c -o hello -lbpf -lelf
# 运行
sudo ./hello
# 在另一个终端,查看日志
sudo cat /sys/kernel/debug/tracing/trace_pipe
使用 bpftrace 进行快速实验
# 按进程统计系统调用
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[comm] = count(); }'
# 分析 CPU 使用情况
sudo bpftrace -e 'profile:hz:99 { @[stack] = count(); }'
# 跟踪文件打开
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s opened %s
", comm, str(args->filename)); }'
未来发展方向
新兴趋势
- Windows eBPF:Microsoft 正在开发 Windows 的 eBPF 支持
- 用户空间 eBPF:在用户空间运行 eBPF 程序(ubpf)
- 硬件卸载:将 eBPF 卸载到 NIC 和其他硬件
- 基于 eBPF 的服务:更多完全基于 eBPF 构建的服务
- 改进的工具:更好的开发工具和调试支持
活跃开发领域
- CO-RE 改进:更好的跨内核版本可移植性
- 新的钩子点:更多用于各种用例的挂载点
- 增强的验证器:更快、功能更强的验证
- Map 增强:新的 map 类型和操作
- 文档:改进的文档和学习资源
行业采用
eBPF 正被主要公司采用:
- Meta:使用 eBPF 进行负载均衡(Katran)和监控
- Google:在 GKE 中使用 eBPF 进行安全和网络
- Netflix:使用 eBPF 进行可观测性和安全
- Cloudflare:使用 eBPF 进行 DDoS 防护和网络
- Isovalent:由 Cilium 创始人创立,推动 eBPF 采用
资源
官方文档
- Linux Kernel eBPF 文档
- libbpf GitHub
- Cilium 文档
学习资源
- eBPF.io - eBPF 官方社区网站
- BPF 性能工具书籍
- Linux Observability with BPF
工具和项目
- bcc - BPF 编译器集合
- bpftrace - 高级跟踪语言
- Cilium - 基于 eBPF 的网络和安全
- libbpf-bootstrap - libbpf 入门指南
社区
- eBPF Slack
- eBPF 峰会
- CNCF eBPF 工作组
结论
eBPF 代表了我们与 Linux 内核交互方式的范式转变。通过提供安全、高效和可编程的内核内部接口,eBPF 使我们在可观测性、网络、安全和性能优化方面能够实现前所未有的能力。
随着生态系统的不断成熟和采用的增长,eBPF 注定要成为云原生基础设施的基础技术,使开发者能够在不修改内核代码的情况下构建更高效、安全和可观测的系统。
eBPF 的未来是光明的,持续的开发正在扩展其在操作系统和用例方面的能力。无论您是构建监控工具、保护容器还是优化网络性能,eBPF 都为创新提供了强大的基础。










