首页
/ eunomia-bpf 项目教程:eBPF 开发入门之 Hello World

eunomia-bpf 项目教程:eBPF 开发入门之 Hello World

2025-07-10 02:02:15作者:江焘钦

前言

eBPF(Extended Berkeley Packet Filter)是 Linux 内核中一项革命性的技术,它允许开发者在不修改内核源代码的情况下,动态地向内核注入自定义代码。这种能力为系统观测、性能分析、安全防护等领域带来了前所未有的可能性。本文将基于 eunomia-bpf 项目,带领读者从零开始编写并运行第一个 eBPF 程序。

eBPF 开发环境准备

在开始 eBPF 开发前,我们需要配置合适的开发环境:

  1. 内核要求:建议使用 Linux 5.15 或 6.2 以上版本的内核,最低要求为 4.8 版本
  2. 工具链安装
    • LLVM 和 Clang:用于编译 eBPF 程序
    • 在 Ubuntu/Debian 上可通过以下命令安装:
      sudo apt install clang llvm
      

eunomia-bpf 工具安装

eunomia-bpf 是一个开源的 eBPF 动态加载运行时和开发工具链,它简化了 eBPF 程序的开发、构建和运行流程。我们需要安装两个核心工具:

  1. ecli:用于运行 eBPF 程序

    wget https://aka.pw/bpf-ecli -O ecli && chmod +x ./ecli
    
  2. ecc:eBPF 编译器工具链

    wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc
    

第一个 eBPF 程序:Hello World

下面是一个最简单的 eBPF 程序示例,它会在每次系统调用 write 时打印一条日志:

/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#define BPF_NO_GLOBAL_DATA
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

typedef unsigned int u32;
typedef int pid_t;
const pid_t pid_filter = 0;

char LICENSE[] SEC("license") = "Dual BSD/GPL";

SEC("tp/syscalls/sys_enter_write")
int handle_tp(void *ctx)
{
    pid_t pid = bpf_get_current_pid_tgid() >> 32;
    if (pid_filter && pid != pid_filter)
        return 0;
    bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid);
    return 0;
}

代码解析

  1. 头文件:包含了必要的 eBPF 开发头文件
  2. 许可证声明:eBPF 程序必须声明许可证
  3. SEC 宏:定义了程序挂载点(这里是 sys_enter_write 系统调用入口)
  4. 核心函数
    • bpf_get_current_pid_tgid():获取当前进程 ID
    • bpf_printk():内核打印函数(有参数限制)

编译与运行

  1. 编译 eBPF 程序

    ./ecc minimal.bpf.c
    

    或使用 Docker:

    docker run -it -v `pwd`/:/src/ ghcr.io/eunomia-bpf/ecc-`uname -m`:latest
    
  2. 运行程序

    sudo ./ecli run package.json
    
  3. 查看输出

    sudo cat /sys/kernel/debug/tracing/trace_pipe | grep "BPF triggered"
    

eBPF 程序基本框架

一个完整的 eBPF 程序通常包含以下要素:

  1. 头文件linux/bpf.hbpf/bpf_helpers.h
  2. 许可证声明:通常是 "Dual BSD/GPL"
  3. BPF 函数
    • 使用 SEC() 宏定义挂载点
    • 返回值通常为整型
  4. 辅助函数:如 bpf_printk()

跟踪点(Tracepoint)简介

Tracepoint 是内核中的静态插桩技术:

  • 在内核源代码中预置的探测点
  • 稳定的 API 接口
  • 常见于系统调用、调度器事件等关键路径
  • 本例中使用的 sys_enter_write 就是一个典型的 tracepoint

项目模板推荐

为了简化 eBPF 项目初始化,eunomia-bpf 提供了多种语言的项目模板:

  1. C + libbpf:传统开发方式
  2. Go + cilium/ebpf:Go 语言开发
  3. Rust + libbpf-rs:Rust 语言开发
  4. C + eunomia-bpf:简化开发流程

这些模板都包含:

  • 一键构建的 Makefile
  • Docker 支持
  • GitHub Actions 集成
  • 完整的依赖配置

总结

通过本文,我们学习了:

  1. eBPF 开发环境的搭建
  2. 使用 eunomia-bpf 工具链开发简单程序
  3. eBPF 程序的基本结构和原理
  4. 如何编译和运行 eBPF 程序

eBPF 技术的强大之处在于它能够在运行时动态修改内核行为,而不需要重新编译或加载内核模块。随着对 eBPF 的深入学习,开发者可以实现更复杂的功能,如系统观测、性能分析等。

建议读者在掌握这个简单示例后,继续探索 eBPF 的更多高级特性,如 perf buffer、map 数据结构等,以充分发挥这项技术的潜力。