eBPF 開發(fā)入門之 helloworld

我是 LEE,老李,一個在 IT 行業(yè)摸爬滾打 16 年的技術老兵。

事件背景

最近一直想寫一個關于 ebpf 的文章,但是不知道從哪里開始寫起。思考良久之后,決定站在一個研發(fā)的角度,求真務實的從入門的角度來介紹下 ebpf。 實際我真正的目的是能夠用好的 cilium 的整個系統(tǒng),但是站在 cilium 角度上看 ebpf,它確實一個黑盒子。emmm.... 這個不符合我的人設,決定今天整理一個文章往里嘗試看看究竟。

廢話不多說,我們從一個非常簡單的 helloworld 的編寫出發(fā)。

前置知識

這個作為 epbf 學習的第一章知識,我相信很多小伙伴跟我一樣都是“白手起家,一窮二白”,害怕 ebpf 整個環(huán)境的復雜性。既然是前置知識,那一定是最簡單的,能夠讓我們一下就懂的。

ebpf 是一個轉(zhuǎn)發(fā)層的驅(qū)動模型,既然是模型,就一定有定義和抽象等概念,我們通過下圖就可以有了第一印象。

ebpf

eBPF 分為用戶空間程序和內(nèi)核程序兩部分

  1. 用戶空間程序負責加載 BPF 字節(jié)碼至內(nèi)核,如需要也會負責讀取內(nèi)核回傳的統(tǒng)計信息或者事件詳情
  2. 內(nèi)核中的 BPF 字節(jié)碼負責在內(nèi)核中執(zhí)行特定事件,如需要也會將執(zhí)行的結(jié)果通過 maps 或者 perf-event 事件發(fā)送至用戶空間
  3. 其中用戶空間程序與內(nèi)核 BPF 字節(jié)碼程序可以使用 map 結(jié)構(gòu)實現(xiàn)雙向通信,這為內(nèi)核中運行的 BPF 字節(jié)碼程序提供了更加靈活的控制

上面的內(nèi)容用大白話說:ebpf 包含用戶層和內(nèi)核層兩層組成。用戶層主要是負責業(yè)務邏輯處理和響應,同時也兼顧著內(nèi)核中的 epbf 的邏輯 bytescode 生成(當然這里可以使用第三方生成),并將 bytescode 注入到內(nèi)核中。內(nèi)核層主要是接受 bytescode,然后在內(nèi)核層內(nèi)完成對 bytescode 執(zhí)行。果真是妥妥的控制與轉(zhuǎn)發(fā)分離的模型,要不然為什么 ebpf 這么高效呢?

上手開發(fā)

環(huán)境準備

我們使用 Ubuntu 20.04 這個系統(tǒng)作為整體開發(fā)環(huán)境,內(nèi)核使用的是 5.4.0。

檢查內(nèi)核

root@ubuntu:/tmp# uname -a
Linux ubuntu 5.4.0-126-generic #142-Ubuntu SMP Fri Aug 26 12:12:57 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

安裝編譯工具和包

# apt install -y bison build-essential cmake flex git libedit-dev pkg-config libmnl-dev \
   python zlib1g-dev libssl-dev libelf-dev libcap-dev libfl-dev llvm clang pkg-config \
   gcc-multilib luajit libluajit-5.1-dev libncurses5-dev libclang-dev clang-tools

安裝內(nèi)核源碼

# apt install linux-source-5.4.0

源碼安裝至 /usr/src/ 目錄下, 使用下面的命里初始化編譯環(huán)境。

# cd /usr/src/linux-source-5.4.0/
#
# tar xvf linux-source-5.4.0.tar.bz2
# cd linux-source-5.4.0
#
# cp -v /boot/config-$(uname -r) .config
# make headers_install && make modules_prepare

開發(fā)代碼

ebpf 是使用內(nèi)核層和用戶兩層,那么我們寫代碼也要實現(xiàn)兩層代碼。

內(nèi)核層代碼:hello_kern.c

#include <linux/bpf.h>
#include "bpf/bpf_helpers.h"

#define SEC(NAME) __attribute__((section(NAME), used))

SEC("tracepoint/syscalls/sys_enter_execve")
int bpf_prog(void *ctx)
{
    char msg[] = "Hello Shengyan.li, I'm in eBPF World\n";

    bpf_trace_printk(msg, sizeof(msg));  /* 把信息打印到 trace 隊列中 */

    return 0;
}

char _license[] SEC("license") = "GPL"; /* 這里很重要,告訴 ebpf 當前的模塊協(xié)議是 GPL,如果不是GPL,libpbf 報錯 err = 22, 退出。*/

用戶層代碼:hello_user.c

#include <stdio.h>
#include "bpf_load.h"

int main(int argc, char **argv)
{
    if(load_bpf_file("hello_kern.o") != 0) /* 加載生成的 bytescode 到內(nèi)核*/
    {
        printf("The kernel didn't load BPF program\n");
        return -1;
    }

    read_trace_pipe();  /* 從 trace 隊列中讀取信息,輸出到 stdout */

    return 0;
}

編譯測試

修改 Makefile
在 /usr/src/linux-source-5.4.0/linux-source-5.4.0/samples/bpf 目錄下,修改 Makefile,然后在對應的位置添加對應內(nèi)容。

hostprogs-y += helloworld
hello-objs := bpf_load.o hello_user.o
always += hello_kern.o

編譯

# cd /usr/src/linux-source-5.4.0/linux-source-5.4.0
# make M=samples/bpf

測試

# cd /usr/src/linux-source-5.4.0/linux-source-5.4.0/samples/bpf
# ./helloworld

當你執(zhí)行上面的 helloworld 命令后,發(fā)現(xiàn)沒有反應,不要慌,這個是正常的。這個時候你只需要在打開一個新會話連接到編譯環(huán)境的機器上,然后在這個新窗口執(zhí)行如下命令:

# watch "ls -l"

之前執(zhí)行 helloworld 的命令的窗口會有如下的輸出:

helloworld

總結(jié)

我們通過上面的操作和使用,基本確認了 epbf 整體開發(fā)流程。 通過簡單的開發(fā),我們基本可以得出下面的幾個結(jié)論:

  1. ebpf 開發(fā)不需要重新編譯內(nèi)核和重啟服務器,能夠非??焖俚牡_發(fā)。
  2. 和 DPDK 的邏輯很像,之前很多積累的知識這里可以無腦復用。
  3. 有很多開發(fā)框架和庫都在使用 ebpf,代碼風險可控。
  4. ebpf 開發(fā)模型沒有那么復雜,還是很簡單,且很有意思的。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容