Fast Packet Processing with eBPF and XDP部分
本文整理了讀了Fast Packet Processing with eBPF and XDP中有關eBPF的部分內容後的相關知識
目錄- BPF產生背景
- BPF是什麼
- eBPF是什麼
- eBPF用途
- eBPF的組成
- 為什麼設計eBPF
- eBPF的優勢
- JIT
- eBPF的結構
- eBPF系統
- eBPF核心驗證器
- eBPF程式是如何以及何時執行的
- eBPF機制思維導圖
- LLVM與Clang
BPF產生背景
- 網際網路流量的增加和資料中心網路中提供的服務的日益複雜要求越來越高的資料包處理速率。
- 服務需求的動態性要求網路快速適應,以保持足夠的服務質量水平,並有效地使用可用資源。
- 計算機網路傳統上是靜態發展的,通訊協議的實現嵌入到網路裝置的硬體中,難以適應當前的需求。
BPF是什麼
- Berkeley Packet Filter (BPF)是由Steven McCanne和Van Jacobson在1992年提出的,作為在Unix BSD系統核心上執行資訊包過濾的解決方案。
- 它由一組指令和一個用於執行用那種語言編寫的程式的虛擬機器(VM)組成。
eBPF是什麼
- Extended Berkeley Packet Filter(eBPF)是Linux核心內的指令集和執行環境。它支援在執行時進行修改、互動和核心程式設計。
- eBPF可以用來程式設計eXpress Data Path (XDP),一個核心網路層,處理資料包接近NIC的快速資料包處理。
- 開發人員可以用C或P4語言編寫程式,然後編譯成eBPF指令,這些指令可以由核心或可程式設計裝置(如SmartNICs)處理。
eBPF用途
- 比如網路監控、網路流量處理、負載平衡和作業系統洞察。有幾家公司已經在Facebook、Netronome和Cloudflare等專案上使用了eBPF。
eBPF的組成
- 一套指令
- 一個虛擬機器(VM),用於執行用該語言編寫的程式。
- BPF定義了基於包的記憶體模型(載入指令在被處理的包中隱式生成),
- 兩個暫存器:累加器(A)和索引表(X)
- 隱含的程式計數器
- 一個臨時的輔助記憶體
為什麼設計eBPF
- BPF不僅對資訊包過濾非常有用,其他領域也可以從它檢測核心的能力中受益。為了將BPF機轉化為通用的核心內虛擬機器,對BPF機及其總體架構進行了大量改進。這個新版本稱為eBPF(擴充套件的BPF),或簡稱BPF,而最初的迭代成為cBPF(經典的BPF)。
eBPF的優勢
- 具有安全檢查
- 應用程式的位元組碼從使用者空間傳輸到核心,然後在核心中對其進行檢查,以確保安全性並防止核心崩潰。
- 簡單且定義良好的指令集
- BPF的即時(JIT)編譯引擎
- eBPF程式在執行時修改(可程式設計的)核心操作,不需要重新編譯核心。
JIT
JIT是一種提高程式執行效率的方法。通常,程式有兩種執行方式:靜態編譯與動態解釋。靜態編譯的程式在執行前全部被翻譯為機器碼,而動態解釋執行的則是一句一句邊執行邊翻譯。
eBPF的結構
- 暫存器的數量從2個增加到11個(其中10個是寫暫存器)
- 暫存器的寬度從32位變為64位
- 指令集現在是64位,新的引擎有512位元組的堆疊。
- 它包括被稱為地圖的全球資料儲存。
- 它添加了幫助函式。
- eBPF的指令集架構(ISA)
ISA被更新為包括遵循C呼叫約定的函式呼叫。 - 引數通過暫存器傳遞給函式。這允許將一個eBPF函式呼叫對映到一個硬體指令,這幾乎沒有開銷。
- eBPF虛擬機器支援動態載入和程式重新載入。
- 暫存器r0儲存函式返回值,表示在計算結束時,在轉發資料包時將採取什麼動作。
- 暫存器r10是唯一的只讀暫存器,它將地址儲存到BPF堆疊中。
- 引數作為暫存器值傳遞給函式。暫存器r1-r5是為此保留的,而暫存器r6-r9在函式呼叫之間預先提供了它們的值。
eBPF系統
-
一個eBPF程式是用高階語言(主要限制為C語言)編寫的。clang編譯器將其轉換為ELF/目的碼。ELF/eBPF載入程式可以使用特殊的系統呼叫將其插入到核心中。在這個過程中,驗證者分析程式,核通過後執行動態轉換(JIT)。程式可以解除安裝到硬體上,否則由處理器本身執行。
-
從3.7版本開始,LLVM編譯器集合有一個用於eBPF平臺的後端。它允許在C的子集中開發eBPF程式,並通過clang編譯器生成eBPF格式的可執行程式碼。
C的這個子集排除了一些系統呼叫和庫,但是它提供了幫助函式來操作eBPF對映和執行其他常見任務。 -
eBPF的一些限制
eBPF只能使用C語言庫的一個子集。例如,printf()函式不可用 不允許使用非靜態全域性變數; 只允許有界迴圈。 堆疊空間被限制為512位元組
eBPF核心驗證器
工作原理
-
驗證者使用兩個方法來決定是否拒絕一個程式。
- 在第一輪中,它使用深度優先搜尋來檢查程式指令是否可以被解析成一個有向無環圖(DAG)。
- 第二步探索從程式的第一條指令開始的所有可能路徑。 -
關於eBPF驗證器還有兩點。
- 第一個與以下事實有關:一些eBPF函式只能由具有GPL相容許可證的程式呼叫。
- 最後,驗證器不允許訪問超出本地變數和資料包界限的記憶體,以確保核心的完整性和安全性。
如果eBPF程式不做這種型別的檢查,那麼驗證者拒絕它,因此它不能載入到核心。
eBPF程式是如何以及何時執行的
要執行一個eBPF程式,首先需要把它附加到允許自定義程式設計的介面上。這個介面稱為鉤子。鉤子允許程式註冊某些事件。如兩個可以附加eBPF程式的Linux核心鉤子:XDP和TC。
eBPF機制思維導圖
LLVM與Clang
什麼是LLVM
LLVM專案是模組化、可重用的編譯器以及工具鏈技術的集合。
LLVM架構
- 不同的前端後端使用統一的中間程式碼LLVM Intermediate Representation (LLVM IR)
- 如果需要支援一種新的程式語言,那麼只需要實現一個新的前端
- 如果需要支援一種新的硬體裝置,那麼只需要實現一個新的後端
- 優化階段是一個通用的階段,它針對的是統一的LLVM IR,不論是支援新的程式語言,還是支援新的硬體裝置,都不需要對優化階段做修改
- 相比之下,GCC的前端和後端沒分得太開,前端後端耦合在了一起。所以GCC為了支援一門新的語言,或者為了支援一個新的目標平臺,就 變得特別困難
- LLVM現在被作為實現各種靜態和執行時編譯語言的通用基礎結構(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
什麼是Clang
LLVM專案的一個子專案,基於LLVM架構的C/C++/Objective-C編譯器前端。
相比於GCC,Clang具有如下優點
- 編譯速度快:在某些平臺上,Clang的編譯速度顯著的快過GCC(Debug模式下編譯OC速度比GGC快3倍)
- 佔用記憶體小:Clang生成的AST所佔用的記憶體是GCC的五分之一左右
- 模組化設計:Clang採用基於庫的模組化設計,易於 IDE 整合及其他用途的重用
- 診斷資訊可讀性強:在編譯過程中,Clang 建立並保留了大量詳細的元資料 (metadata),有利於除錯和錯誤報告
- 設計清晰簡單,容易理解,易於擴充套件增強
Clang與LLVM關係
- LLVM整體架構,前端用的是clang,廣義的LLVM是指整個LLVM架構,一般狹義的LLVM指的是LLVM後端(包含程式碼優化和目的碼生成)。
- 原始碼(c/c++)經過clang--> 中間程式碼(經過一系列的優化,優化用的是Pass) --> 機器碼