3. 开发环境搭建:内核版本要求、工具链安装与 Hello World
搭建一个稳定、可复现的开发环境是 eBPF 程序开发的基石。本章将详细讲解 Linux 内核版本的选择标准、核心工具链的安装步骤,并通过一个完整的 Hello World 程序验证环境是否就绪。
3.1 Linux 内核版本要求
eBPF 的功能随内核版本演进不断扩展。不同内核版本对 eBPF 特性的支持程度差异显著,选择正确的内核版本可以避免大量兼容性问题。
| 内核版本 | 关键 eBPF 特性 | 推荐用途 |
|---|---|---|
| 4.4 – 4.9 | 基础 eBPF、kprobes、tracepoints | 仅用于学习基础概念,不推荐新项目 |
| 4.15 – 4.20 | BPF 映射类型扩展、bpf_tail_call | 可运行简单追踪程序 |
| 5.4 – 5.10 | BPF 迭代器、BPF 链接、CO-RE 初步支持 | 生产环境最低推荐版本 |
| 5.15 – 6.x | BPF 可移植性(CO-RE)、BPF 调度器、BPF 内存分配器 | 强烈推荐:开发与生产首选 |
版本选择建议: 本课程所有示例均基于 Linux 5.15 LTS 及以上内核。如果你使用的是 Ubuntu 20.04+ 或 CentOS 8+,建议升级至 5.15 或更高版本。使用
uname -r 命令检查当前内核版本。
3.2 安装必要的工具链
eBPF 程序开发需要一套完整的编译与运行时工具链。下面按组件逐一说明安装方法。
3.2.1 Clang / LLVM
eBPF 程序通常使用 C 语言编写,然后通过 Clang 编译为 BPF 字节码。LLVM 版本至少需要 11.0 以上才能完整支持 BPF 后端特性。
# Ubuntu / Debian
sudo apt-get update
sudo apt-get install -y clang llvm
# CentOS / RHEL / Fedora
sudo dnf install -y clang llvm
# 验证版本
clang --version | head -n 1
llc --version | head -n 1
3.2.2 libbpf
libbpf 是用户态加载 eBPF 程序的核心库,提供 BPF 系统调用封装、BPF 对象管理、CO-RE 支持等功能。推荐从内核源码树中获取最新版本。
# 从内核源码中提取 libbpf
git clone --depth 1 https://github.com/libbpf/libbpf.git
cd libbpf/src
make
sudo make install
# 或者使用包管理器(版本可能较旧)
# Ubuntu: sudo apt-get install -y libbpf-dev
# Fedora: sudo dnf install -y libbpf-devel
3.2.3 bpftool
bpftool 是调试和管理 eBPF 程序的瑞士军刀,支持查看已加载的程序、BPF 映射、性能统计等。
# 从内核源码编译
git clone --depth 1 https://github.com/libbpf/bpftool.git
cd bpftool/src
make
sudo make install
# 验证安装
bpftool version
bpftool feature list
常见问题: 如果
bpftool feature list 显示某些特性不可用,请检查内核是否开启了 CONFIG_DEBUG_INFO_BTF=y 和 CONFIG_BPF_SYSCALL=y。这是 CO-RE 和 BTF 支持的前提。
3.3 工具链架构概览
下图展示了从 C 源码到内核执行的完整工具链流程:
3.4 编写第一个 Hello World eBPF 程序
我们将编写一个最简单的 eBPF 程序:在每次 execve 系统调用发生时打印一条消息。程序包含两部分:内核态 BPF 代码和用户态加载器。
3.4.1 内核态 BPF 程序(hello.bpf.c)
// hello.bpf.c
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
char LICENSE[] SEC("license") = "GPL";
SEC("tracepoint/syscalls/sys_enter_execve")
int hello_execve(void *ctx)
{
bpf_printk("Hello, eBPF! execve called\n");
return 0;
}
3.4.2 用户态加载器(hello.c)
// hello.c
#include <stdio.h>
#include <unistd.h>
#include <bpf/libbpf.h>
#include "hello.skel.h"
int main(void)
{
struct hello_bpf *skel;
int err;
// 打开并加载 BPF 程序
skel = hello_bpf__open();
if (!skel) {
fprintf(stderr, "Failed to open BPF skeleton\n");
return 1;
}
err = hello_bpf__load(skel);
if (err) {
fprintf(stderr, "Failed to load BPF skeleton\n");
goto cleanup;
}
err = hello_bpf__attach(skel);
if (err) {
fprintf(stderr, "Failed to attach BPF skeleton\n");
goto cleanup;
}
printf("eBPF program loaded and attached! Press Ctrl+C to exit.\n");
// 等待用户中断
while (1) {
sleep(1);
}
cleanup:
hello_bpf__destroy(skel);
return err;
}
3.4.3 编译与运行
使用 Makefile 自动化编译流程:
# Makefile
CLANG ?= clang
LLVM_STRIP ?= llvm-strip
BPFTOOL ?= bpftool
all: hello
hello: hello.bpf.o hello.skel.h hello.c
$(CC) -o hello hello.c -lbpf -lelf -lz
hello.bpf.o: hello.bpf.c
$(CLANG) -g -O2 -target bpf -c hello.bpf.c -o hello.bpf.o
hello.skel.h: hello.bpf.o
$(BPFTOOL) gen skeleton hello.bpf.o > hello.skel.h
clean:
rm -f hello hello.bpf.o hello.skel.h
运行步骤:
- 执行
make编译程序。 - 以 root 权限运行
sudo ./hello。 - 在另一个终端执行任意命令(如
ls),触发execve系统调用。 - 查看内核 trace 日志:
sudo cat /sys/kernel/debug/tracing/trace_pipe。
预期输出类似:
# cat /sys/kernel/debug/tracing/trace_pipe
<...> [000] .... 12345.678901: 0: Hello, eBPF! execve called
<...> [001] .... 12346.789012: 0: Hello, eBPF! execve called
3.5 环境验证清单
完成上述步骤后,使用以下清单确认环境完全就绪:
| 检查项 | 验证命令 | 预期结果 |
|---|---|---|
| 内核版本 | uname -r |
>= 5.15 |
| Clang 版本 | clang --version |
>= 11.0 |
| libbpf 安装 | pkg-config --libs libbpf |
输出 -lbpf |
| bpftool 可用 | bpftool version |
显示版本号 |
| BTF 支持 | bpftool feature list | grep BTF |
显示 CONFIG_DEBUG_INFO_BTF=y |
| Hello World 运行 | sudo ./hello + 查看 trace |
trace_pipe 输出日志 |