eBPF 入门:从 Hello World 到性能分析
eBPF 是近年来 Linux 内核最激动人心的技术之一。它让你在不修改内核源码、不加载内核模块的前提下,安全地在内核中运行沙箱化程序。从网络、安全到可观测性,eBPF 的应用场景在快速扩展。
为什么是 eBPF
传统的性能分析工具大多依赖内核暴露的固定接口:/proc、perf、ftrace。这些工具能提供的信息有限,而且往往需要较高的权限。eBPF 提供了编程能力——你可以写 C 代码,编译成 BPF 字节码,然后 attach 到内核的 hook 点上,实时采集任意数据。
环境准备
在 Ubuntu 22.04 上安装依赖:
apt install -y clang llvm libbpf-dev bpftool linux-tools-common
确认内核版本(需要 5.x+):
uname -r
# 5.15.0-46-generic ✓
Hello, eBPF World
第一个程序:统计系统中 execve 系统调用的次数。
// counter.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, 1);
__type(key, __u32);
__type(value, __u64);
} exec_count SEC(".maps");
SEC("tracepoint/syscalls/sys_enter_execve")
int count_execve(void *ctx) {
__u32 key = 0;
__u64 *val = bpf_map_lookup_elem(&exec_count, &key);
if (val) *val += 1;
return 0;
}
char LICENSE[] SEC("license") = "GPL";
实际应用场景
我在生产环境里用到 eBPF 的几个场景:
- 慢查询追踪:通过挂载
uretprobe在数据库客户端库的查询函数上,收集所有超过 100ms 的 SQL 语句 - 网络延迟分析:用
kprobe在tcp_sendmsg和tcp_cleanup_rbuf上计算请求-响应时间,定位瓶颈在应用层还是网络层 - 容器资源审计:按 cgroup 聚合 CPU 和内存使用,发现了一些"安静泄漏"的服务
踩过的坑
- BTF 兼容性:不同内核版本的 BTF 信息有差异,跨版本部署时需要 CO-RE(Compile Once, Run Everywhere)技术,用
libbpf的 vmlinux.h 而非内核头文件 - Verifier 限制:BPF verifier 对程序复杂度有严格要求——最大 4096 条指令,不允许无界循环。复杂的逻辑需要拆成多个 BPF 程序,用 BPF map 传递中间结果
- 性能开销:虽然 eBPF 在内核态运行,但每个事件触发都会有一定开销。对于高频事件(如网络包),需要配合
bpf_ringbuf做批量读取,在用户态采样而不是逐个事件处理
推荐阅读
eBPF 的学习曲线确实陡峭——你需要理解 C 语言、内核内部机制和 BPF 指令集。但一旦掌握,你就拥有了一把可以观察和操控内核的瑞士军刀。