eBPF 入門開發實踐教程二:Hello World,基本框架和開發流程

本文是 eBPF 入門開發實踐教程的第二篇,我們將重點關注如何編寫一個簡單的 eBPF 程序,並通過實際例子演示整個開發流程。在閱讀本教程之前,建議您先學習第一篇教程,以便對 eBPF 的基本概念有個大致的瞭解。

在開發 eBPF 程序時,有多種開發框架可供選擇,如 BCC(BPF Compiler Collection)、libbpf、cilium/ebpf、eunomia-bpf、Coolbpf 等。雖然不同工具的特點各異,但它們的基本開發流程大致相同。在接下來的內容中,我們將深入瞭解這些流程,並以 Hello World 程序爲例,帶領讀者逐步掌握 eBPF 開發的基本技巧。

本教程將幫助您瞭解 eBPF 程序的基本結構、編譯和加載過程、用戶空間與內核空間的交互方式以及調試與優化技巧。通過學習本教程,您將掌握 eBPF 開發的基本知識,併爲後續進一步學習和實踐奠定堅實的基礎。

eBPF 開發環境準備與基本開發流程

在開始編寫 eBPF 程序之前,我們需要準備一個合適的開發環境,並瞭解 eBPF 程序的基本開發流程。本部分將詳細介紹這些內容。

安裝必要的軟件和工具

要開發 eBPF 程序,您需要安裝以下軟件和工具:

eBPF 程序主要由兩部分構成:內核態部分和用戶態部分。內核態部分包含 eBPF 程序的實際邏輯,用戶態部分負責加載、運行和監控內核態程序。當您選擇了合適的開發框架後,您可以開始進行用戶態和內核態程序的開發。以 BCC 工具爲例,我們將介紹 eBPF 程序的基本開發流程:

  1. 安裝 BCC 工具:根據您的 Linux 發行版,按照 BCC 官方文檔的指南安裝 BCC 工具和相關依賴。

  2. 編寫 eBPF 程序(C 語言):使用 C 語言編寫一個簡單的 eBPF 程序,例如 Hello World 程序。該程序可以在內核空間執行並完成特定任務,如統計網絡數據包數量。

  3. 編寫用戶態程序(Python 或 C 等):使用 Python、C 等語言編寫用戶態程序,用於加載、運行 eBPF 程序以及與之交互。在這個程序中,您需要使用 BCC 提供的 API 來加載和操作內核態的 eBPF 程序。

  4. 編譯 eBPF 程序:使用 BCC 工具,將 C 語言編寫的 eBPF 程序編譯成內核可以執行的字節碼。BCC 會在運行時動態從源碼編譯 eBPF 程序。

  5. 加載並運行 eBPF 程序:在用戶態程序中,使用 BCC 提供的 API 加載編譯好的 eBPF 程序到內核空間,然後運行該程序。

  6. 與 eBPF 程序交互:用戶態程序通過 BCC 提供的 API 與 eBPF 程序交互,實現數據收集、分析和展示等功能。例如,您可以使用 BCC API 讀取 eBPF 程序中的 map 數據,以獲取網絡數據包統計信息。

  7. 卸載 eBPF 程序:當不再需要 eBPF 程序時,用戶態程序應使用 BCC API 將其從內核空間卸載。

  8. 調試與優化:使用 bpftool 等工具進行 eBPF 程序的調試和優化,提高程序性能和穩定性。

通過以上流程,您可以使用 BCC 工具開發、編譯、運行和調試 eBPF 程序。請注意,其他框架(如 libbpf、cilium/ebpf 和 eunomia-bpf、Coolbpf)的開發流程大致相似但略有不同,因此在選擇框架時,請參考相應的官方文檔和示例。eunomia-bpf 是一個開源的 eBPF 動態加載運行時和開發工具鏈,它的目的是簡化 eBPF 程序的開發、構建、分發、運行。它基於 libbpf 的 CO-RE 輕量級開發框架,支持通過用戶態 WASM 虛擬機控制 eBPF 程序的加載和執行,並將預編譯的 eBPF 程序打包爲通用的 JSON 或 WASM 模塊進行分發。我們會使用 eunomia-bpf 進行演示。

下載安裝 eunomia-bpf 開發工具

可以通過以下步驟下載和安裝 eunomia-bpf:

下載 ecli 工具,用於運行 eBPF 程序:

$ wget https://aka.pw/bpf-ecli -O ecli && chmod +x ./ecli
$ ./ecli -h
Usage: ecli [--help] [--version] [--json] [--no-cache] url-and-args

下載編譯器工具鏈,用於將 eBPF 內核代碼編譯爲 config 文件或 WASM 模塊:

$ wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc && chmod +x ./ecc
$ ./ecc -h
eunomia-bpf compiler
Usage: ecc [OPTIONS] <SOURCE_PATH> [EXPORT_EVENT_HEADER]
....

也可以使用 docker 鏡像進行編譯:

$ docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest # 使用 docker 進行編譯。`pwd` 應該包含 *.bpf.c 文件和 *.h 文件。
export PATH=PATH:~/.eunomia/bin
Compiling bpf object...
Packing ebpf object and config into /src/package.json...

Hello World - minimal eBPF program

我們會先從一個簡單的 eBPF 程序開始,它會在內核中打印一條消息。我們會使用 eunomia-bpf 的編譯器工具鏈將其編譯爲 bpf 字節碼文件,然後使用 ecli 工具加載並運行該程序。作爲示例,我們可以暫時省略用戶態程序的部分。

```c
/* 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 from PID %d.\n", pid);
 return 0;
}

這段程序通過定義一個 handle_tp 函數並使用 SEC 宏把它附加到 sys_enter_write tracepoint(即在進入 write 系統調用時執行)。該函數通過使用 bpf_get_current_pid_tgid 和 bpf_printk 函數獲取調用 write 系統調用的進程 ID,並在內核日誌中打印出來。

要編譯和運行這段程序,可以使用 ecc 工具和 ecli 命令。首先使用 ecc 編譯程序:

$ ecc hello.bpf.c
Compiling bpf object...
Packing ebpf object and config into package.json...

或使用 docker 鏡像進行編譯:

docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest

然後使用 ecli 運行編譯後的程序:

$ sudo ecli run ./package.json
Runing eBPF program...

運行這段程序後,可以通過查看 /sys/kernel/debug/tracing/trace_pipe 文件來查看 eBPF 程序的輸出:

$ sudo cat /sys/kernel/debug/tracing/trace_pipe
           <...>-3840345 [010] d... 3220701.101143: bpf_trace_printk: write system call from PID 3840345.
           <...>-3840345 [010] d... 3220701.101143: bpf_trace_printk: write system call from PID 3840345.

eBPF 程序的基本框架

如上所述, eBPF 程序的基本框架包括:

tracepoints

跟蹤點(tracepoints)是內核靜態插樁技術,跟蹤點在技術上只是放置在內核源代碼中的跟蹤函數,實際上就是在源碼中插入的一些帶有控制條件的探測點,這些探測點允許事後再添加處理函數。比如在內核中,最常見的靜態跟蹤方法就是 printk,即輸出日誌。又比如:在系統調用、調度程序事件、文件系統操作和磁盤 I/O 的開始和結束時都有跟蹤點。於 2009 年在 Linux 2.6.32 版本中首次提供。跟蹤點是一種穩定的 API,數量有限。

GitHub 模板:輕鬆構建 eBPF 項目和開發環境

面對創建一個 eBPF 項目,您是否對如何開始搭建環境以及選擇編程語言感到困惑?別擔心,我們爲您準備了一系列 GitHub 模板,以便您快速啓動一個全新的 eBPF 項目。只需在 GitHub 上點擊 Use this template 按鈕,即可開始使用。

這些啓動模板包含以下功能:

通過將現有倉庫設置爲模板,您和其他人可以快速生成具有相同基礎結構的新倉庫,從而省去了手動創建和配置的繁瑣過程。藉助 GitHub 模板倉庫,開發者可以專注於項目的核心功能和邏輯,而無需爲基礎設置和結構浪費時間。更多關於模板倉庫的信息,請參閱官方文檔:https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository

總結

eBPF 程序的開發和使用流程可以概括爲如下幾個步驟:

需要注意的是,BPF 程序的執行是在內核空間進行的,因此需要使用特殊的工具和技術來編寫、編譯和調試 BPF 程序。eunomia-bpf 是一個開源的 BPF 編譯器和工具包,它可以幫助開發者快速和簡單地編寫和運行 BPF 程序。

本教程的文檔和源代碼已經全部開源,可以在 https://github.com/eunomia-bpf/bpf-developer-tutorial 中查看。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/YUbaBSTfSsg57lPzw34Hrw