eBPF 入门:从 Hello World 到性能分析

· #eBPF #Linux #可观测性

eBPF 是近年来 Linux 内核最激动人心的技术之一。它让你在不修改内核源码、不加载内核模块的前提下,安全地在内核中运行沙箱化程序。从网络、安全到可观测性,eBPF 的应用场景在快速扩展。

为什么是 eBPF

传统的性能分析工具大多依赖内核暴露的固定接口:/procperfftrace。这些工具能提供的信息有限,而且往往需要较高的权限。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 的几个场景:

踩过的坑

  1. BTF 兼容性:不同内核版本的 BTF 信息有差异,跨版本部署时需要 CO-RE(Compile Once, Run Everywhere)技术,用 libbpf 的 vmlinux.h 而非内核头文件
  2. Verifier 限制:BPF verifier 对程序复杂度有严格要求——最大 4096 条指令,不允许无界循环。复杂的逻辑需要拆成多个 BPF 程序,用 BPF map 传递中间结果
  3. 性能开销:虽然 eBPF 在内核态运行,但每个事件触发都会有一定开销。对于高频事件(如网络包),需要配合 bpf_ringbuf 做批量读取,在用户态采样而不是逐个事件处理

推荐阅读

eBPF 的学习曲线确实陡峭——你需要理解 C 语言、内核内部机制和 BPF 指令集。但一旦掌握,你就拥有了一把可以观察和操控内核的瑞士军刀。