【從零開始的eBPF】跑一個helloworld程式

来源:https://www.cnblogs.com/yux1a0/archive/2023/06/03/17454211.html
-Advertisement-
Play Games

最近在研究ebpf的應用,網上對較低版本的內核和centos操作系統的相關資料較少,這裡記錄一個自己環境配置&編譯運行一個ebpf的helloworld程式的過程。 > 環境是centos7.9,虛擬機安裝記憶體需要分配高一些,後續編譯llvm很吃性能 # 基礎依賴安裝 ## 升級內核版本 ebpf需 ...


最近在研究ebpf的應用,網上對較低版本的內核和centos操作系統的相關資料較少,這裡記錄一個自己環境配置&編譯運行一個ebpf的helloworld程式的過程。

環境是centos7.9,虛擬機安裝記憶體需要分配高一些,後續編譯llvm很吃性能

基礎依賴安裝

升級內核版本

ebpf需要至少內核是4.6+以上的版本,這裡選擇了4.18版本的內核

下載4.18版本內核的安裝包,下載鏈接

# 安裝4.18版本的內核
yum install -y kernel-ml-4.18.0-1.el7.elrepo.x86_64.rpm
# 修改啟動內核順序
yum install -y grub2-pc
grub-set-default 'CentOS Linux (4.18.0-1.el7.elrepo.x86_64) 7 (Core)'

重啟後可以確認下是否切換到4.18版本的內核

uname -sr
# Linux 4.18.0-1.el7.elrepo.x86_64

安裝gcc

編譯llvm 10+需要高版本gcc,這裡使用scl軟體集的方式升級gcc到7.3.1

yum install centos-release-scl
yum install devtoolset-7  -y
scl enable devtoolset-7 bash
echo "source /opt/rh/devtoolset-7/enable" >> ~/.bash_profile
source /opt/rh/devtoolset-7/enable
# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/opt/rh/devtoolset-7/root/usr/libexec/gcc/x86_64-redhat-linux/7/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-languages=c,c++,fortran,lto --prefix=/opt/rh/devtoolset-7/root/usr --mandir=/opt/rh/devtoolset-7/root/usr/share/man --infodir=/opt/rh/devtoolset-7/root/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --enable-plugin --with-linker-hash-style=gnu --enable-initfini-array --with-default-libstdcxx-abi=gcc4-compatible --with-isl=/builddir/build/BUILD/gcc-7.3.1-20180303/obj-x86_64-redhat-linux/isl-install --enable-libmpx --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)

安裝llvm10+

libbpf框架的最新版本需要llvm10+的編譯支持,這裡下載了11版本,解壓後編譯安裝 鏈接

這裡make的需要的時間很長,建議掛著編譯去睡覺

unzip llvm-project-release-11.x.zip
cd llvm-project-release-11.x
mkdir build
cd build/
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_RTTI=ON -DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi" -G "Unix Makefiles" ../llvm
make
make install

藉助libbpf-bootstrap腳手架跑一個helloworld程式

搭建腳手架

libbpf-bootstrap項目提供了一個快速構建ebpf程式的框架,包括libbpf和bpftool兩大工具;項目包含一系列示常式序在examples/c文件夾中,並提供了一個相對通用的Makefile可以供我們瞭解一個ebpf程式是如何編譯起來的
項目地址

克隆libbpf-bootstrap項目,並更新子項目

git clone https://github.com/libbpf/libbpf-bootstrap.git
git submodule update --init --recursive

編寫一個helloworld程式

現在可以在examples/c文件夾下新建兩個文件,分別命名為hello.bpf.chello.c

// hello.bpf.c
#define BPF_NO_GLOBAL_DATA
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_execve")

int bpf_prog(void *ctx) {
  char msg[] = "Hello, World!";
  __bpf_printk("invoke bpf_prog: %s\n", msg);
  return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";
// hello.c
#include <stdio.h>
#include <unistd.h>
#include <sys/resource.h>
#include <bpf/libbpf.h>
#include "hello.skel.h"

static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
    return vfprintf(stderr, format, args);
}

int main(int argc, char **argv)
{
    struct hello_bpf *skel;
    int err;

    libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
    /* Set up libbpf errors and debug info callback */
    libbpf_set_print(libbpf_print_fn);

    /* Open BPF application */
    skel = hello_bpf__open();
    if (!skel) {
        fprintf(stderr, "Failed to open BPF skeleton\n");
        return 1;
    }   

    /* Load & verify BPF programs */
    err = hello_bpf__load(skel);
    if (err) {
        fprintf(stderr, "Failed to load and verify BPF skeleton\n");
        goto cleanup;
    }

    /* Attach tracepoint handler */
    err = hello_bpf__attach(skel);
    if (err) {
        fprintf(stderr, "Failed to attach BPF skeleton\n");
        goto cleanup;
    }

    printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` "
           "to see output of the BPF programs.\n");

    for (;;) {
        /* trigger our BPF program */
        fprintf(stderr, ".");
        sleep(1);
    }

cleanup:
    hello_bpf__destroy(skel);
    return -err;
}

helloworld的程式hook了execve系統調用,在進程創建時會調用execve系統調用進入內核空間,此時被註入內核空間的ebpf位元組碼列印出一個字元串。

需要註意的是:

  • 這個列印行為並不是在用戶空間進行的,需要通過/sys/kernel/debug/tracing/trace_pipe文件觀察列印輸出
  • hello.bpf.c文件中在#include <bpf/bpf_helpers.h>之前定義了一個#define BPF_NO_GLOBAL_DATA巨集,這裡是關閉ebpf的全局變數功能,因為低版本的內核不支持這個特性,項目的示例minimalminimal_legacy很明確的標註了這個問題

This version of minimal is modified to allow running on even older kernels that do not allow global variables. bpf_printk uses global variables unless BPF_NO_GLOBAL_DATA is defined before including bpf_helpers.h. Additionally, the global variable my_pid has been replaced with an array of one element to hold the process pid.

編譯運行helloworld程式

將自己的構建目標加入Makefile的APPS變數中,運行make $obj即可編譯自己的ebpf程式

# Makefile
APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc ksyscall hello

運行make hello後,列印出整個項目的編譯流程,主要分為三個步驟如下

  • 編譯libbpf
  • 編譯bpftool
  • 編譯hello

當然編譯一個ebpf程式的過程不是這麼簡單的過程,helloworld程式的具體編譯的過程會在後文介紹

# make hello
which: no cargo in (/opt/rh/devtoolset-7/root/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
  MKDIR    .output
  MKDIR    .output/libbpf
  LIB      libbpf.a
  MKDIR    /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/bpf.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/btf.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/libbpf.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/libbpf_errno.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/netlink.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/nlattr.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/str_error.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/libbpf_probes.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/bpf_prog_linfo.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/btf_dump.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/hashmap.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/ringbuf.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/strset.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/linker.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/gen_loader.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/relo_core.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/usdt.o
  CC       /root/libbpf-bootstrap/examples/c/.output//libbpf/staticobjs/zip.o
  AR       /root/libbpf-bootstrap/examples/c/.output//libbpf/libbpf.a
  INSTALL  bpf.h libbpf.h btf.h libbpf_common.h libbpf_legacy.h bpf_helpers.h bpf_helper_defs.h bpf_tracing.h bpf_endian.h bpf_core_read.h skel_internal.h libbpf_version.h usdt.bpf.h
  INSTALL  /root/libbpf-bootstrap/examples/c/.output//libbpf/libbpf.pc
  INSTALL  /root/libbpf-bootstrap/examples/c/.output//libbpf/libbpf.a
  MKDIR    bpftool
  BPFTOOL  bpftool/bootstrap/bpftool
...                        libbfd: [ OFF ]
...               clang-bpf-co-re: [ on  ]
...                          llvm: [ on  ]
...                        libcap: [ OFF ]
  MKDIR    /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/include/bpf
  INSTALL  /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/include/bpf/hashmap.h
  INSTALL  /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/include/bpf/relo_core.h
  INSTALL  /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/include/bpf/libbpf_internal.h
  MKDIR    /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/
  MKDIR    /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/
  MKDIR    /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/bpf.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/btf.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/libbpf.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/libbpf_errno.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/netlink.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/nlattr.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/str_error.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/libbpf_probes.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/bpf_prog_linfo.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/btf_dump.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/hashmap.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/ringbuf.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/strset.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/linker.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/gen_loader.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/relo_core.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/staticobjs/usdt.o
  AR       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/libbpf/libbpf.a
  INSTALL  bpf.h libbpf.h btf.h libbpf_common.h libbpf_legacy.h bpf_helpers.h bpf_helper_defs.h bpf_tracing.h bpf_endian.h bpf_core_read.h skel_internal.h libbpf_version.h usdt.bpf.h
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/main.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/common.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/json_writer.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/gen.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/btf.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/xlated_dumper.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/btf_dumper.o
  CC       /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/disasm.o
  LINK     /root/libbpf-bootstrap/examples/c/.output/bpftool/bootstrap/bpftool
  BPF      .output/hello.bpf.o
  GEN-SKEL .output/hello.skel.h
  CC       .output/hello.o
  BINARY   hello

這裡我們的helloworld程式就編譯好了,嘗試運行一下,需要使用root用戶:

# ./hello
libbpf: loading object 'hello_bpf' from buffer
libbpf: elf: section(2) .symtab, size 120, link 1, flags 0, type=2
libbpf: elf: section(3) tracepoint/syscalls/sys_enter_execve, size 200, link 0, flags 6, type=1
libbpf: sec 'tracepoint/syscalls/sys_enter_execve': found program 'bpf_prog' at insn offset 0 (0 bytes), code size 25 insns (200 bytes)
libbpf: elf: section(4) .rodata.str1.1, size 35, link 0, flags 32, type=1
libbpf: elf: section(5) license, size 13, link 0, flags 3, type=1
libbpf: license of hello_bpf is Dual BSD/GPL
libbpf: elf: section(6) .BTF, size 438, link 0, flags 0, type=1
libbpf: elf: section(7) .BTF.ext, size 160, link 0, flags 0, type=1
libbpf: looking for externs among 5 symbols...
libbpf: collected 0 externs total
libbpf: map '.rodata.str1.1' (global data): at sec_idx 4, offset 0, flags 80.
libbpf: map 0 is ".rodata.str1.1"
libbpf: map '.rodata.str1.1': skipped auto-creating...
Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` to see output of the BPF programs.

此時打開另一個終端查看/sys/kernel/debug/tracing/trace_pipe文件可以看到程式列印:

# cat /sys/kernel/debug/tracing/trace_pipe
           <...>-79115 [000] .... 67971.749552: 0: invoke bpf_prog: Hello, World!
           <...>-79117 [000] .... 67971.753866: 0: invoke bpf_prog: Hello, World!
           <...>-79119 [000] .... 67971.757380: 0: invoke bpf_prog: Hello, World!
           <...>-79120 [000] .... 67971.761711: 0: invoke bpf_prog: Hello, World!
           <...>-79121 [000] .... 67971.764028: 0: invoke bpf_prog: Hello, World!
           <...>-79123 [000] .... 67971.769068: 0: invoke bpf_prog: Hello, World!
           <...>-79124 [000] .... 67971.771786: 0: invoke bpf_prog: Hello, World!
           <...>-79126 [000] .... 67971.776243: 0: invoke bpf_prog: Hello, World!
           <...>-79127 [000] .... 67971.779373: 0: invoke bpf_prog: Hello, World!
           <...>-79129 [000] .... 67971.784824: 0: invoke bpf_prog: Hello, World!
           <...>-79131 [000] .... 67971.791846: 0: invoke bpf_prog: Hello, World!
           <...>-79132 [000] .... 67979.733858: 0: invoke bpf_prog: Hello, World!

這裡我們的helloworld程式就運行成功了

to be continued


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [toc] 這篇文章是我之前總結的一篇文章,因為整理博客的原因,原有博客已經註銷,但這篇文章對一些讀者很有用,所以現在新瓶裝舊酒重新整理回來分享給大家。 最近一段時間生產環境頻繁出問題,每次都會生成一個hs_err_pid*.log文件,因為工作內容的原因,在此之前並沒有瞭解過相關內容,趁此機會學習 ...
  • 隨著技術的發展,ASP.NET Core MVC也推出了好長時間,經過不斷的版本更新迭代,已經越來越完善,本系列文章主要講解ASP.NET Core MVC開發B/S系統過程中所涉及到的相關內容,適用於初學者,在校畢業生,或其他想從事ASP.NET Core MVC 系統開發的人員。 經過前幾篇文章... ...
  • 以前在隨筆《Winform開發框架之客戶關係管理系統(CRM)的開發總結系列1-界面功能展示 》的幾篇隨筆中介紹過基於WInform開發框架開發的CRM系統,系統的功能主要也是圍繞著客戶相關信息來進行管理的。本篇隨筆介紹在最新的《SqlSugar開發框架》中整合CRM系統模塊的功能。 ...
  • 1. 擴展方法 擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種靜態方法,但可以像擴展類型上的實例方法一樣進行調用。 對於用 C#、F# 和 Visual Basic 編寫的客戶端代碼,調用擴展方法與調用在類型中定義的方法沒有明顯區別 ...
  • 最近ET做熱更重載dll的時候,返回登陸會重新檢測新的dll,首次登錄之前已經Assembly.Load()過一次dll,第二次返回登陸再次load dll到記憶體中,Invoke執行方法的時候,異常了,有些方法執行了,有些未執行,於是查資料,看到些老資料說Assembly.Load重覆載入同名dll ...
  • # 1. 與 .NET Core 緩存的關係和差異 ABP 框架中的緩存系統核心包是 [Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching) ,而對於分散式緩存的支持,abp 官方提供了基於 Redis 的方案,需要安裝 ...
  • 人臉識別技術在現代社會中扮演著越來越重要的角色,比如人臉識別門禁、人臉識別支付、甚至人臉識別網站登錄等。 最近有群友問.NET有沒有人臉識別的組件,小編查閱相關資料介紹下麵幾種.NET人臉識別組件供大家參考。 **1、Microsoft Azure Face API** 簡介:Microsoft A ...
  • ## 01|修改Win用戶名 打開運行,輸入cmd,回車; 輸入control userpasswords2,回車; 點擊屬性,修改用戶名,點擊確定; 打開運行,輸入regedit,回車; 定位到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\Cur ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...