0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

BTF 實踐指南[譯]

Linux閱碼場 ? 來源:Linux閱碼場 ? 2023-03-02 10:27 ? 次閱讀

BPF 是 Linux 內(nèi)核中基于寄存器的虛擬機,可安全、高效和事件驅(qū)動的方式執(zhí)行加載至內(nèi)核的字節(jié)碼。與內(nèi)核模塊不同,BPF 程序經(jīng)過驗證以確保它們終止并且不包含任何可能鎖定內(nèi)核的循環(huán)。BPF 程序允許調(diào)用的內(nèi)核函數(shù)也受到限制,以確保最大的安全性以防止非法的訪問。

盡管 BPF 為編寫事件驅(qū)動的內(nèi)核空間代碼提供了一種有效的解決方案,但開發(fā)人員的體驗仍無法與其他編程語言或框架相提并論。BPF 開發(fā)的兩個最重要的問題是缺乏簡單的調(diào)試和可移植性。

為了緩解這些問題,我們轉(zhuǎn)向 BTF[1]。BTF 是針對 BPF 程序的類型信息進行編碼文件格式,通過 BPF 程序的類型信息進行編碼,為程序提供更好的內(nèi)?。╥ntrospection)和可見性。本文我們將介紹 BPF 的典型局限性以及如何使用 BTF 來克服。

請注意,本文使用術(shù)語 BPF 來表示 eBPF[2](擴展的 Berkeley 數(shù)據(jù)包過濾器),eBPF 擴展 “ 經(jīng)典 ” cBPF。

1. BPF 的常見限制

在 BPF 程序的開發(fā)和運行過程中,我們會經(jīng)常會面臨調(diào)試限制和可移植性問題,如前所述。

1.1 調(diào)試限制

幾乎所有現(xiàn)代編程語言都有對應(yīng)的調(diào)試器,通過調(diào)試器可以幫助我們更好了解正在運行的程序。例如,GDB[3] 是 C 和 C++ 的常用調(diào)試器,除其他外,基于 GDB 我們可以打印正在運行的程序中的變量值。

722d9c0e-b813-11ed-bfe3-dac502259ad0.png圖 GDB 變量打印

但是很不幸,BPF 程序并沒有類似的這樣的工具。盡管檢查數(shù)據(jù)只是調(diào)試的一小部分,但為 BPF 實現(xiàn)類似的結(jié)果可以為未來的廣泛調(diào)試工具打開一扇門。為了實現(xiàn)這一點,BPF 需要知道關(guān)于程序的相關(guān)的部分元數(shù)據(jù)。

這類關(guān)于類型信息的元數(shù)據(jù),正是 BTF 封裝的內(nèi)容。

1.2 可移植性

BPF 程序在內(nèi)核空間中運行,可以訪問內(nèi)部內(nèi)核狀態(tài)和數(shù)據(jù)結(jié)構(gòu)。但是,并沒有辦法保證內(nèi)核數(shù)據(jù)結(jié)構(gòu)和類型在不同內(nèi)核版本是相同的,甚至相同內(nèi)核版本的不同機器之間也可能不同(這可能取決于內(nèi)核編譯選項)。這意味著在一臺機器上編譯的 BPF 程序并不能保證在另一臺機器上正確運行。

假設(shè) BPF 程序正在從內(nèi)核結(jié)構(gòu)中讀取一個字段,該字段位于距結(jié)構(gòu)開頭的偏移量 8 處?,F(xiàn)在在更高版本的內(nèi)核中,在該變量之前添加了其他字段,導(dǎo)致訪問的字段的偏移量變成了 24,這會導(dǎo)致 BPF 程序在偏移量 8 讀取的數(shù)據(jù)可能為垃圾數(shù)據(jù)。類似情況,也可能會發(fā)生某些字段最終得到在后續(xù)內(nèi)核版本中的重命名。例如,在內(nèi)核版本 4.6 和 4.7 之間,thread_struct 的 fs 字段可能會重命名為 fsbase 。最后,還可能是因為配置禁用了某些功能并編譯了部分結(jié)構(gòu),導(dǎo)致可能 BPF 程序在不同的內(nèi)核配置上運行。

所有上述這些場景的存在,意味著你不能在當前機器上編譯 BPF 程序并將二進制文件分發(fā)到其他系統(tǒng)。

一個標準的解決方案是使用 BPF Compiler Collection (BCC)[4]。使用 BCC,你通常將 BPF 程序作為純字符串嵌入到用戶空間程序(例如,Python 程序)中。在目標機器上執(zhí)行期間,BCC 使用其嵌入式 Clang/LLVM 組合并使用本地安裝的內(nèi)核頭文件動態(tài)編譯程序。

然而,這種方法引入了更多問題。首先,Clang/LLVM 組合非常龐大,將其嵌入到應(yīng)用程序中會導(dǎo)致二進制文件大小過大。它還占用大量資源,并且會在編譯期間耗盡大量資源。最后,這種方法需要在目標機器上安裝內(nèi)核頭文件,但情況可能并非總是如此。

解決方案是 BPF CO-RE(一次編譯 —— 隨處運行)。使用 BTF,我們可以消除在目標機器上安裝內(nèi)核頭文件或?qū)?Clang/LLVM 嵌入應(yīng)用程序并在目標機器上編譯的需要。

2. BTF 是什么?

如前所述,BTF 是編碼 BPF 程序和 map 結(jié)構(gòu)等相關(guān)的調(diào)試信息的元數(shù)據(jù)格式。BTF 可以將元數(shù)據(jù)數(shù)據(jù)類型、函數(shù)信息和行信息編碼成一種緊湊的格式。

在非 BPF 程序中,這些元數(shù)據(jù)通常使用 DWARF[5] 格式存儲。但是,DWARF 格式的實現(xiàn)還是相當復(fù)雜和冗長,并且由于其在大小方面的開銷,使其不適合包含在內(nèi)核中。而 BTF 是一種緊湊而簡單的格式,讓其可以包含在內(nèi)核鏡像中。

BTF 使用少數(shù)類型描述[6] 符之一表示每種數(shù)據(jù)類型:

  • BTF_KIND_INT
  • BTF_KIND_PTR
  • BTF_KIND_ARRAY
  • BTF_KIND_STRUCT
  • 等等

類型信息存儲在生成的 ELF 的 .BTF 部分中。除了類型描述符之外,此部分還對字符串進行編碼。函數(shù)和行信息存儲在 .BTF.ext 部分中。

關(guān)于 BTF 的詳細說明,可以查看 Linux Kernel 文檔[7]。

3. BTF 快速入門

3.1 BPF 快速入門

現(xiàn)在讓我們通過使用 BTF 漂亮地打印[8] BPF map 的教程進行更多實踐,從而顯著改進調(diào)試。

要開始,我們需要在啟用 CONFIG_DEBUG_INFO_BTF 選項的情況下編譯 Linux 內(nèi)核。大多數(shù)發(fā)行版都啟用了此選項,但你可以通過運行以下命令進行檢查:

$zgrepCONFIG_DEBUG_INFO_BTF=y/proc/config.gz
#可選:grep CONFIG_DEBUG_INFO_BTF=y /boot/config*

當然,我們還需要在計算機上安裝 Clang 和 LLVM[9]

由于我們需要將編寫 XDP[10] 程序來處理網(wǎng)絡(luò)設(shè)備上的數(shù)據(jù)包,因此創(chuàng)建一個虛擬網(wǎng)絡(luò)接口[11] 是個好主意,這樣就不會最終失去物理接口中的互聯(lián)網(wǎng)連接。設(shè)置虛擬接口的最簡單方法是使用此 repo[12]。

克隆 repo 并設(shè)置一個名為 test1 的虛擬接口:

$gitclonegit@github.com:xdp-project/xdp-tutorial.git
$cdxdp-tutorial/testenv
$sudo./testenv.shsetup--name=test1--legacy-ip

現(xiàn)在編寫一個 BPF 程序來計算接口上接收到的 IPv4 和 IPv6 數(shù)據(jù)包的數(shù)量。文件 xdp_count.c 的文件內(nèi)容如下:

#include
#include
#include
#include

structbpf_map_defSEC("maps")cnt={
.type=BPF_MAP_TYPE_ARRAY,
.key_size=sizeof(__u32),
.value_size=sizeof(long),
.max_entries=2,
};


SEC("xdp_count")
intxdp_count_prog(structxdp_md*ctx)
{
void*data_end=(void*)(long)ctx->data_end;
void*data=(void*)(long)ctx->data;
__u32ipv6_key=0;
__u32ipv4_key=1;
long*value;
__u16h_proto;
structethhdr*eth=data;
if(data+sizeof(structethhdr)>data_end)//Thischeckisnecessarytopassverification
returnXDP_DROP;

h_proto=eth->h_proto;
if(h_proto==htons(ETH_P_IPV6)){//CheckifIPv6packet
value=bpf_map_lookup_elem(&cnt,&ipv6_key);
if(value)
*value+=1;
returnXDP_PASS;
}
value=bpf_map_lookup_elem(&cnt,&ipv4_key);
if(value)
*value+=1;
returnXDP_PASS;

}

char_license[]SEC("license")="GPL";

在前面的代碼中,名為 cnt 的 BPF map 存儲數(shù)據(jù)包的數(shù)量。cnt 是兩個元素的數(shù)組。IPv6 數(shù)據(jù)包的數(shù)量存儲在 key 0 中,IPv4 數(shù)據(jù)包的數(shù)量存儲在 key 1 中。

使用 Clang 編譯代碼:

$clang-O2-Wall-g-targetbpf-cxdp_count.c-oxdp_count.o

接下來,使用 bpftool 加載程序:

$sudobpftoolprogloadxdp_count.o/sys/fs/bpf/xdp_counttypexdp

運行以下命令并記下我們剛加載的程序的 ID 和程序正在使用的 map 的 ID:

$sudobpftoolproglist
7249ce06-b813-11ed-bfe3-dac502259ad0.png圖 bpftool prog 列表的輸出

我們還可以通過運行 sudo bpftool map list 來獲取 map ID。

72555a8c-b813-11ed-bfe3-dac502259ad0.png

圖 bpftool map list 的內(nèi)容輸出

此命令為我們提供 map 的名稱、類型、鍵大小、值大小和最大條目數(shù)。

現(xiàn)在,將 BPF 程序附加到網(wǎng)絡(luò)設(shè)備。

$sudobpftoolnetattachxdpgenericiddevtest1

將 program_id 替換為程序的 ID,并將 device_name 替換為程序附加到的網(wǎng)絡(luò)設(shè)備的名稱(例如 enp34s0)。

現(xiàn)在,向該設(shè)備發(fā)送一些數(shù)據(jù)包。測試環(huán)境腳本已經(jīng)提供了一個方便的 ping 命令來執(zhí)行此操作:

$sudo./testenv.shping#ForIPv6
$sudo./testenv.shping--legacy-ip#ForIPv4

打印 map 并檢查處理的數(shù)據(jù)包情況:

$sudobpftoolmapdumpid

726968d8-b813-11ed-bfe3-dac502259ad0.png

圖 map 的打印值

如圖所示,map 中有兩個預(yù)期的元素。這些值采用十六進制格式,并且還取決于運行機器的字節(jié)順序。在截圖中,它是小端格式,這意味著已經(jīng)處理了 22 個 IPv6 和 4 個 IPv4 數(shù)據(jù)包。

很明顯,結(jié)果是十六進制的,小端格式,一看就不好調(diào)試。因此,我們需要使用 BTF 對 map 進行注釋,以便更好地展示。

如下更改 cnt 的聲明并將新代碼保存在 xdp_count_btf.c 中 -

...
struct{
__uint(type,BPF_MAP_TYPE_ARRAY);
__type(key,__u32);
__type(value,long);
__uint(max_entries,2);
}cntSEC(".maps");
...

請注意,部分名稱現(xiàn)在為 .maps,并且地圖本身已使用啟用 BTF 的宏 __uint__type 進行了注釋。

使用 Clang 編譯代碼:

clang-O2-Wall-g-targetbpf-cxdp_count_btf.c-oxdp_count_btf.o

使用 -g 標志將創(chuàng)建調(diào)試信息并生成 BTF。請注意,之前也使用了 -g 標志,因為 libbpf需要它[13] 來加載程序;然而,以前 map 沒有被 BTF 注釋,所以 bpftool 不能夠優(yōu)雅地進行打印。

驗證 BTF 部分是否存在于生成的目標文件中。

$llvm-objdump-hxdp_count_btf.o

728674be-b813-11ed-bfe3-dac502259ad0.png

圖 llvm-objdump 的輸出

如前所述,.BTF 部分包含類型和字符串數(shù)據(jù),.BTF.ext 部分對 func_info 和 line_info 數(shù)據(jù)進行編碼。

首先,卸載前面的 BPF 程序。

$sudobpftoolnetdetachxdpgenericdevtest1

然后按照類似的過程加載新程序并將其附加到接口,然后對接口發(fā)送一些數(shù)據(jù):

$sudobpftoolprogloadxdp_count_btf.o/sys/fs/bpf/xdp_count_btftypexdp
$sudobpftoolproglist
$sudobpftoolnetattachxdpgenericiddevtest1
$sudo./testenv.shping
$sudo./testenv.shping--legacy-ip

最后,打印新程序?qū)?yīng)的 map 。如果一切順利,這一次的輸出會有很大的不同。

7292e6cc-b813-11ed-bfe3-dac502259ad0.png

圖 map 的結(jié)構(gòu)的打印值

它不僅以 JSON 格式打印得很漂亮,而且值也是十進制的,使其更具可讀性和易懂性。

3.1 BTF 和 CO-RE

如前所述,BTF 可以啟用 CO-RE 使 BPF 程序可移植到不同的內(nèi)核版本或用戶配置。我們也可以通過生成內(nèi)核本身的 BTF 信息來消除對本地內(nèi)核頭文件的需求:

$bpftoolbtfdumpfile/sys/kernel/btf/vmlinuxformatc>vmlinux.h

上述命令將創(chuàng)建一個巨大的 vmlinux.h 文件,其中包含所有內(nèi)核類型,包括作為 UAPI 的一部分公開的類型、內(nèi)部類型和通過 kernel-devel 可用的類型,以及一些其他地方不可用的更多內(nèi)部類型。在 BPF 程序中,我們可以只 #include "vmlinux.h" 并刪除其他內(nèi)核頭文件,如 等。

擺脫內(nèi)核頭文件依賴性只是 BTF 可以實現(xiàn)的目標的冰山一角。如需 BTF 和 CO-RE 的詳盡解釋,可以閱讀這篇文章[14]。

4. 結(jié)論

BTF 是一個非常強大的工具,可以使 BPF 程序更易于調(diào)試和移植。由于它是一項相對較新的技術(shù),因此開發(fā)仍在進行中,你可以期待在未來看到大量改進。

本文讓你大致了解 BTF 可以實現(xiàn)什么。你可能已經(jīng)了解了 BPF 的缺點、BPF 是什么以及如何使用 BTF 注解 map 和打印 map 結(jié)構(gòu)。最后,你還了解了 BTF 如何充當通過 CO-RE 增強可移植性的起點。

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5359

    瀏覽量

    120808
  • 編程語言
    +關(guān)注

    關(guān)注

    10

    文章

    1949

    瀏覽量

    34850
  • 虛擬機
    +關(guān)注

    關(guān)注

    1

    文章

    919

    瀏覽量

    28323

原文標題:BTF 實踐指南[譯]

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    請問I2C的BTF位什么時候置1?

    參考手冊上的原話是“如果在上一次數(shù)據(jù)傳輸結(jié)束之前TxE位已置1,但數(shù)據(jù)字節(jié)尚未寫入DR寄存器,則BTF位會置1,而接口會一直延長SCL低電平,等待I2C_DR寄存器被寫入,以將BTF清零?!?我結(jié)合
    發(fā)表于 05-06 06:35

    高速PCB布線實踐指南

    高速PCB布線實踐指南_(上)
    發(fā)表于 08-20 16:26

    數(shù)字信號處理實踐方法——EC.Ifeachor著,羅鵬飛等

    本帖最后由 怒放_的_生命 于 2013-10-30 22:31 編輯 數(shù)字信號處理實踐方法——EC.Ifeachor著,羅鵬飛等。。網(wǎng)上最高清的掃描版本。?!稊?shù)字信號處理實踐方法(第二
    發(fā)表于 10-28 12:36

    機器學(xué)習(xí)實踐指南——案例應(yīng)用解析

    機器學(xué)習(xí)實踐指南——案例應(yīng)用解析
    發(fā)表于 04-13 16:40

    高速PCB布線實踐指南

    高速PCB布線實踐指南
    發(fā)表于 06-12 21:52

    開關(guān)電源設(shè)計指南_(英)布朗(Brown.M.)著/徐德鴻等

    電子發(fā)燒友網(wǎng)站提供《開關(guān)電源設(shè)計指南_(英)布朗(Brown.M.)著/徐德鴻等.txt》資料免費下載
    發(fā)表于 03-02 15:02 ?0次下載

    高速PCB布線實踐指南

    高速PCB布線實踐指南,只是理論知識,不是實際軟件操作
    發(fā)表于 12-11 16:59 ?0次下載

    Lua游戲開發(fā)實踐指南 - 詳章

    Lua游戲開發(fā)實踐指南 - 詳章.pdf
    發(fā)表于 01-14 16:13 ?29次下載

    最重要的電磁兼容設(shè)計指南_龍鳳呈祥_

    最重要的電磁兼容設(shè)計指南【龍鳳呈祥
    發(fā)表于 01-14 16:22 ?0次下載

    高速PCB布線實踐指南

    高速PCB布線實踐指南,好東西,喜歡的朋友可以下載來學(xué)習(xí)。
    發(fā)表于 01-18 15:41 ?0次下載

    高速PCB布線實踐指南_(上冊)

    高速PCB布線實踐指南_(上冊),好東西,喜歡的朋友可以下載來學(xué)習(xí)。
    發(fā)表于 01-18 15:41 ?0次下載

    Linux系統(tǒng)命令及shell腳本實踐指南

    Linux系統(tǒng)命令及shell腳本實踐指南資料下載。
    發(fā)表于 06-01 14:47 ?28次下載

    《STM32 Cortex-M3權(quán)威指南》宋巖

    《STM32 Cortex-M3權(quán)威指南》宋巖
    發(fā)表于 12-30 10:24 ?58次下載

    SAN管理最佳實踐指南

    電子發(fā)燒友網(wǎng)站提供《SAN管理最佳實踐指南.pdf》資料免費下載
    發(fā)表于 08-29 09:20 ?0次下載
    SAN管理最佳<b class='flag-5'>實踐</b><b class='flag-5'>指南</b>

    SAN設(shè)計和最佳實踐指南

    電子發(fā)燒友網(wǎng)站提供《SAN設(shè)計和最佳實踐指南.pdf》資料免費下載
    發(fā)表于 09-01 11:02 ?0次下載
    SAN設(shè)計和最佳<b class='flag-5'>實踐</b><b class='flag-5'>指南</b>