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 源码到内核执行的完整工具链流程:

eBPF 工具链架构图 C 源码 Clang / LLVM BPF 字节码 内核验证器 libbpf(用户态) BPF 系统调用 bpftool 说明:Clang 将 C 源码编译为 BPF 字节码,内核验证器检查安全性,libbpf 负责加载与映射管理,bpftool 用于调试。

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
运行步骤:
  1. 执行 make 编译程序。
  2. 以 root 权限运行 sudo ./hello。
  3. 在另一个终端执行任意命令(如 ls),触发 execve 系统调用。
  4. 查看内核 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 输出日志