深入理解 Linux eBPF:一個完整閱讀清單(轉載)【轉】

轉自:https://blog.csdn.net/21cnbao/article/details/95585483

linux eBPF是3.17內核開始引入的一個全新設計,代碼目錄主要在kernel/bpf 下,它的全稱是 extended BPF(eBPF), 目前關於eBPF的資料還比較亂,很難得看到一篇對ebpf總結的那麼全的文章,轉載自此:
譯自:https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/作者: Quentin Monnet
原創:LCTT https://linux.cn/article-9507-1.html譯者: qhwdw

查看更多eBPF文章別忘了掃描旁邊二維碼關注"linux閱碼場"哦~

什麼是 BPF?

BPF,及伯克利包過濾器Berkeley Packet Filter,最初構想提出於 1992 年,其目的是爲了提供一種過濾包的方法,並且要避免從內核空間到用戶空間的無用的數據包複製行爲。它最初是由從用戶空間注入到內核的一個簡單的字節碼構成,它在那個位置利用一個校驗器進行檢查 —— 以避免內核崩潰或者安全問題 —— 並附着到一個套接字上,接着在每個接收到的包上運行。幾年後它被移植到 Linux 上,並且應用於一小部分應用程序上(例如,tcpdump)。其簡化的語言以及存在於內核中的即時編譯器(JIT),使 BPF 成爲一個性能卓越的工具。

然後,在 2013 年,Alexei Starovoitov 對 BPF 進行徹底地改造,並增加了新的功能,改善了它的性能。這個新版本被命名爲 eBPF (意思是 “extended BPF”),與此同時,將以前的 BPF 變成 cBPF(意思是 “classic” BPF)。新版本出現瞭如映射和尾調用tail call這樣的新特性,並且 JIT 編譯器也被重寫了。新的語言比 cBPF 更接近於原生機器語言。並且,在內核中創建了新的附着點。

感謝那些新的鉤子,eBPF 程序纔可以被設計用於各種各樣的情形下,其分爲兩個應用領域。其中一個應用領域是內核跟蹤和事件監控。BPF 程序可以被附着到探針(kprobe),而且它與其它跟蹤模式相比,有很多的優點(有時也有一些缺點)。

另外一個應用領域是網絡編程。除了套接字過濾器外,eBPF 程序還可以附加到 tc(Linux 流量控制工具)的入站或者出站接口上,以一種很高效的方式去執行各種包處理任務。這種使用方式在這個領域開創了一個新的天地。

並且 eBPF 通過使用爲 IO Visor 項目開發的技術,使它的性能進一步得到提升:也爲 XDP(“eXpress Data Path”)添加了新的鉤子,XDP 是不久前添加到內核中的一種新式快速路徑。XDP 與 Linux 棧組合,然後使用 BPF ,使包處理的速度更快。

甚至一些項目,如 P4、Open vSwitch,考慮[2] 或者開始去接洽使用 BPF。其它的一些,如 CETH、Cilium,則是完全基於它的。BPF 是如此流行,因此,我們可以預計,不久之後,將圍繞它有更多工具和項目出現 …

深入理解字節碼

就像我一樣:我的一些工作(包括 BEBA[3])是非常依賴 eBPF 的,並且在這個網站上以後的幾篇文章將關注於這個主題。按理說,在深入到細節之前,我應該以某種方式去介紹 BPF —— 我的意思是,真正的介紹,在第一節所提供的簡要介紹上更多地介紹在 BPF 上開發的新功能:什麼是 BPF 映射?尾調用?內部結構是什麼樣子?等等。但是,在這個網站上已經有很多這個主題的介紹了,而且,我也不希望去寫另一篇 “BPF 介紹” 的重複文章。

畢竟,我花費了很多的時間去閱讀和學習關於 BPF 的知識,因此,在這裏我們將要做什麼呢,我收集了非常多的關於 BPF 的閱讀材料:介紹、文檔,也有教程或者示例。這裏有很多的材料可以去閱讀,但是,爲了去閱讀它,首先要去 找到 它。因此,爲了能夠幫助更多想去學習和使用 BPF 的人,現在的這篇文章給出了一個資源清單。這裏有各種閱讀材料,它可以幫你深入理解內核字節碼的機制。

資源

簡介

這篇文章中下面的鏈接提供了 BPF 的基本概述,或者,一些與它密切相關的一些主題。如果你對 BPF 非常陌生,你可以在這些介紹文章中挑選出一篇你喜歡的文章去閱讀。如果你已經理解了 BPF,你可以針對特定的主題去閱讀,下面是閱讀清單。

關於 BPF

關於 eBPF 的常規介紹:

BPF 內部結構:

IO Visor 博客[21] 有一些關於 BPF 的值得關注技術文章。它們中的一些包含了一點營銷討論。

內核跟蹤:總結了所有的已有的方法,包括 BPF:

  • 邂逅 eBPF 和內核跟蹤[22] (Viller Hsiao, July 2016):

    Kprobes、uprobes、ftrace

  • Linux 內核跟蹤[23](Viller Hsiao, July 2016):

    Systemtap、Kernelshark、trace-cmd、LTTng、perf-tool、ftrace、hist-trigger、perf、function tracer、tracepoint、kprobe/uprobe …

關於 事件跟蹤和監視,Brendan Gregg 大量使用了 eBPF,並且就其使用 eBPFR 的一些案例寫了極好的文檔。如果你正在做一些內核跟蹤方面的工作,你應該去看一下他的關於 eBPF 和火焰圖相關的博客文章。其中的大多數都可以 從這篇文章中[24] 訪問,或者瀏覽他的博客。

介紹 BPF,也介紹 Linux 網絡的一般概念:

硬件卸載offload(LCTT 譯註:“卸載”是指原本由軟件來處理的一些操作交由硬件來完成,以提升吞吐量,降低 CPU 負荷。):

  • eBPF 與 tc 或者 XDP 一起支持硬件卸載,開始於 Linux 內核版本 4.9,是由 Netronome 提出的。這裏是關於這個特性的介紹:eBPF/XDP 硬件卸載到 SmartNICs[27](Jakub Kicinski 和 Nic Viljoen, netdev 1.2, Tokyo, October 2016)
  • 一年後出現的更新版:

    綜合 XDP 卸載——處理邊界案例[28](Jakub Kicinski 和 Nic Viljoen,netdev 2.2 ,Seoul,November 2017)

  • 我現在有一個簡短的,但是在 2018 年的 FOSDEM 上有一個更新版:

    XDP 硬件卸載的挑戰[29](Quentin Monnet,FOSDEM 2018,Brussels,February 2018)

關於 cBPF:

關於 XDP

  • 在 IO Visor 網站上的 XDP 概述[36]。
  • eXpress Data Path (XDP)[37]  (Tom Herbert, Alexei Starovoitov, March 2016):

    這是第一個關於 XDP 的演講。

  • BoF - BPF 能爲你做什麼?[38]  (Brenden Blanco, LinuxCon, Toronto, August 2016)。
  • eXpress Data Path[39] (Brenden Blanco, Linux Meetup at Santa Clara, July 2016):

    包含一些(有點營銷的意思?)基準測試結果!使用單一核心:

    • ip 路由丟棄: ~3.6 百萬包每秒(Mpps)
    • 使用 BPF,tc(使用 clsact qdisc)丟棄: ~4.2 Mpps
    • 使用 BPF,XDP 丟棄:20 Mpps (CPU 利用率 < 10%)
    • XDP 重寫轉發(在端口上它接收到的包):10 Mpps

    (測試是用 mlx4 驅動程序執行的)。

  • Jesper Dangaard Brouer 有幾個非常好的幻燈片,它可以從本質上去理解 XDP 的內部結構。
    • XDP − eXpress Data Path,介紹及將來的用法[40] (September 2016):

      “Linux 內核與 DPDK 的鬥爭” 。未來的計劃(在寫這篇文章時)它用 XDP 和 DPDK 進行比較。

    • 網絡性能研討[41]  (netdev 1.2, Tokyo, October 2016):

      關於 XDP 內部結構和預期演化的附加提示。

    • XDP – eXpress Data Path, 可用於 DDoS 防護[42] (OpenSourceDays, March 2017):

      包含了關於 XDP 的詳細情況和使用案例,以及 性能測試 的 性能測試結果 和 代碼片斷,以及使用 eBPF/XDP(基於一個 IP 黑名單模式)的用於 基本的 DDoS 防護。

    • 內存 vs. 網絡,激發和修復內存瓶頸[43] (LSF Memory Management Summit, March 2017):

      提供了許多 XDP 開發者當前所面對 內存問題 的許多細節。不要從這一個開始,但如果你已經理解了 XDP,並且想去了解它在頁面分配方面的真實工作方式,這是一個非常有用的資源。

    • XDP 能爲其它人做什麼[44](netdev 2.1, Montreal, April 2017),及 Andy Gospodarek:

      普通人怎麼開始使用 eBPF 和 XDP。這個演講也由 Julia Evans 在 她的博客[45] 上做了總結。

    • XDP 能爲其它人做什麼[46],第二版(netdev 2.2, Seoul, November 2017),同一個作者:

      該演講的修訂版本,包含了新的內容。

(Jesper 也創建了並且嘗試去擴展了有關 eBPF 和 XDP 的一些文檔,查看 相關節[47]。)

  • XDP 研討 — 介紹、體驗和未來發展[48](Tom Herbert, netdev 1.2, Tokyo, October 2016)

    在這篇文章中,只有視頻可用,我不知道是否有幻燈片。

  • 在 Linux 上進行高速包過濾[49] (Gilberto Bertin, DEF CON 25, Las Vegas, July 2017)

    在 Linux 上的最先進的包過濾的介紹,面向 DDoS 的保護、討論了關於在內核中進行包處理、內核旁通、XDP 和 eBPF。

關於 基於 eBPF 或者 eBPF 相關的其它組件

  • 在邊界上的 P4[50] (John Fastabend, May 2016):

    提出了使用 P4,一個包處理的描述語言,使用 BPF 去創建一個高性能的可編程交換機。

  • 如果你喜歡音頻的演講,這裏有一個相關的 OvS Orbit 片斷(#11),叫做 在邊界上的 P4[51],日期是 2016 年 8 月。OvS Orbit 是對 Ben Pfaff 的訪談,它是 Open vSwitch 的其中一個核心維護者。在這個場景中,John Fastabend 是被訪談者。
  • P4, EBPF 和 Linux TC 卸載[52] (Dinan Gunawardena 和 Jakub Kicinski, August 2016):

    另一個 P4 的演講,一些有關於 Netronome 的 NFP(網絡流處理器)架構上的 eBPF 硬件卸載的因素。

  • Cilium 是一個由 Cisco 最先發起的技術,它依賴 BPF 和 XDP 去提供 “基於 eBPF 程序即時生成的,用於容器的快速內核強制的網絡和安全策略”。這個項目的代碼[53] 在 GitHub 上可以訪問到。Thomas Graf 對這個主題做了很多的演講:

    在上述不同的演講中重複了大量的內容;嫌麻煩就選最近的一個。Daniel Borkmann 作爲 Google 開源博客的特邀作者,也寫了 Cilium 簡介[59]。

  • 這裏也有一個關於 Cilium 的播客節目:一個是 OvS Orbit episode (#4)[60],它是 Ben Pfaff 訪談 Thomas Graf (2016 年 5 月),和 另外一個 Ivan Pepelnjak 的播客[61],仍然是 Thomas Graf 關於 eBPF、P4、XDP 和 Cilium 方面的(2016 年 10 月)。
  • Open vSwitch (OvS),它是 Open Virtual Network(OVN,一個開源的網絡虛擬化解決方案)相關的項目,正在考慮在不同的層次上使用 eBPF,它已經實現了幾個概念驗證原型:

    據我所知,這些 eBPF 的使用案例看上去僅處於提議階段(並沒有合併到 OvS 的主分支中),但是,看它帶來了什麼將是非常值得關注的事情。

  • XDP 的設計對分佈式拒絕訪問(DDoS)攻擊是非常有用的。越來越多的演講都關注於它。例如,在 2017 年 4 月加拿大蒙特利爾舉辦的 netdev 2.1 會議上,來自 Cloudflare 的人們的講話(XDP 實踐:將 XDP 集成到我們的 DDoS 緩解管道[64])或者來自 Facebook 的(Droplet:由 BPF + XDP 驅動的 DDoS 對策[65])都存在這樣的很多使用案例。
  • Kubernetes 可以用很多種方式與 eBPF 交互。這裏有一篇關於 在 Kubernetes 中使用 eBPF[66] 的文章,它解釋了現有的產品(Cilium、Weave Scope)如何支持 eBPF 與 Kubernetes 一起工作,並且進一步描述了,在容器部署環境中,eBPF 感興趣的交互內容是什麼。
  • CETH for XDP[67] (Yan Chan 和 Yunsong Lu、Linux Meetup、Santa Clara、July 2016):

    CETH,是由 Mellanox 發起的,爲實現更快的網絡 I/O 而主張的通用以太網驅動程序架構。

  • VALE 交換機[68],另一個虛擬交換機,它可以與 netmap 框架結合,有 一個 BPF 擴展模塊[69]。
  • Suricata,一個開源的入侵檢測系統,它的旁路捕獲旁特性依賴於 XDP。有一些關於它的資源:

    當使用原生驅動的 XDP 時,這個項目要求實現非常高的性能。

  • InKeV:對於 DCN 的內核中分佈式網絡虛擬化[75] (Z. Ahmed, M. H. Alizai 和 A. A. Syed, SIGCOMM, August 2016):

    InKeV 是一個基於 eBPF 的虛擬網絡、目標數據中心網絡的數據路徑架構。它最初由 PLUMgrid 提出,並且聲稱相比基於 OvS 的 OpenStack 解決方案可以獲得更好的性能。

  • gobpf - 在 Go 中使用 eBPF[76] (Michael Schubert, fosdem17, Brussels, Belgium, February 2017):

    “一個來自 Go 庫,可以去創建、加載和使用 eBPF 程序”

  • ply[77] 是爲 Linux 實現的一個小而靈活的開源動態 跟蹤器,它的一些特性非常類似於 bcc 工具,是受 awk 和 dtrace 啓發,但使用一個更簡單的語言。它是由 Tobias Waldekranz 寫的。
  • 如果你讀過我以前的文章,你可能對我在這篇文章中的討論感興趣,使用 eBPF 實現 OpenState 接口[78],關於包狀態處理,在 fosdem17 中。

文檔

一旦你對 BPF 是做什麼的有一個大體的理解。你可以拋開一般的演講而深入到文檔中了。下面是 BPF 的規範和功能的最全面的文檔,按你的需要挑一個開始閱讀吧!

關於 BPF

  • BPF 的規範(包含 classic 和 extended 版本)可以在 Linux 內核的文檔中,和特定的文件 linux/Documentation/networking/filter.txt[79] 中找到。BPF 使用以及它的內部結構也被記錄在那裏。此外,當加載 BPF 代碼失敗時,在這裏可以找到 被校驗器拋出的錯誤信息,這有助於你排除不明確的錯誤信息。
  • 此外,在內核樹中,在 eBPF 那裏有一個關於 常見問答 的文檔,它在文件 linux/Documentation/bpf/bpf_design_QA.txt[80] 中。
  • … 但是,內核文檔是非常難懂的,並且非常不容易閱讀。如果你只是去查找一個簡單的 eBPF 語言的描述,可以去 IO Visor 的 GitHub 倉庫,那兒有 它的概括性描述[81]。
  • 順便說一下,IO Visor 項目收集了許多 關於 BPF 的資源。大部分分別在 bcc 倉庫的 文檔目錄[82] 中,和 bpf-docs 倉庫[83] 的整個內容中,它們都在 GitHub 上。注意,這個非常好的 BPF 參考指南[84] 包含一個詳細的 BPF C 和 bcc Python 的 helper 的描述。
  • 想深入到 BPF,那裏有一些必要的 Linux 手冊頁。第一個是 bpf(2) man 頁面[85] 關於 bpf() 系統調用,它用於從用戶空間去管理 BPF 程序和映射。它也包含一個 BPF 高級特性的描述(程序類型、映射等等)。第二個是主要是處理希望附加到 tc 接口的 BPF 程序:它是 tc-bpf(8) man 頁面[86],是 使用 BPF 和 tc 的一個參考,並且包含一些示例命令和參考代碼。
  • Jesper Dangaard Brouer 發起了一個 更新 eBPF Linux 文檔 的嘗試,包含 不同的映射。他有一個草案[87],歡迎去貢獻。一旦完成,這個文檔將被合併進 man 頁面並且進入到內核文檔。
  • Cilium 項目也有一個非常好的 BPF 和 XDP 參考指南[88],它是由核心的 eBPF 開發者寫的,它被證明對於 eBPF 開發者是極其有用的。
  • David Miller 在 xdp-newbies[89] 郵件列表中發了幾封關於 eBPF/XDP 內部結構的富有啓發性的電子郵件。我找不到一個單獨的地方收集它們的鏈接,因此,這裏是一個列表:

    最後一個可能是目前來說關於校驗器的最佳的總結。

  • Ferris Ellis 發佈的 一個關於 eBPF 的系列博客文章[93]。作爲我寫的這個短文,第一篇文章是關於 eBPF 的歷史背景和未來期望。接下來的文章將更多的是技術方面,和前景展望。
  • 每個內核版本的 BPF 特性列表[94] 在 bcc 倉庫中可以找到。如果你想去知道運行一個給定的特性所要求的最小的內核版本,它是非常有用的。我貢獻和添加了鏈接到提交中,它介紹了每個特性,因此,你也可以從那裏很容易地去訪問提交歷史。

關於 tc

當爲了網絡目的結合使用 BPF 與 tc (Linux 流量控制traffic control工具)時,它可用於收集 tc 的常規功能的信息。這裏有幾個關於它的資源。

關於 XDP

  • 對於 XDP 的一些 進展中的文檔(包括規範)[102] 已經由 Jesper Dangaard Brouer 啓動,並且意味着將成爲一個協作工作。正在推進的(2016 年 9 月):你期望它去改變,並且或許在一些節點上移動(Jesper 稱爲貢獻[103],如果你想去改善它)。
  • 自來 Cilium 項目的 BPF 和 XDP 參考指南[104] … 好吧,這個名字已經說明了一切。

關於 P4 和 BPF

P4[105] 是一個用於指定交換機行爲的語言。它可以爲多種目標硬件或軟件編譯。因此,你可能猜到了,這些目標中的一個就是 BPF … 僅部分支持的:一些 P4 特性並不能被轉化到 BPF 中,並且,用類似的方法,BPF 可以做的事情,而使用 P4 卻不能表達出現。不過,P4 與 BPF 使用 的相關文檔,被隱藏在 bcc 倉庫中[106]。這個改變在 P4_16 版本中,p4c 引用的編輯器包含 一個 eBPF 後端[107]。

教程

Brendan Gregg 爲想要 使用 bcc 工具 跟蹤和監視內核中的事件的人制作了一個非常好的 教程。第一個教程是關於如何使用 bcc 工具[108],它有許多章節,可以教你去理解怎麼去使用已有的工具,而 針對 Python 開發者的一篇[109] 專注於開發新工具,它總共有十七節 “課程”。

Sasha Goldshtein 也有一些 Linux 跟蹤研究材料[110] 涉及到使用幾個 BPF 工具進行跟蹤。

Jean-Tiare Le Bigot 的另一篇文章提供了一個詳細的(和有指導意義的)使用 perf 和 eBPF 去設置一個低級的跟蹤器[111] 的示例。

對於網絡相關的 eBPF 使用案例也有幾個教程。有一些值得關注的文檔,包括一篇 eBPF 卸載入門指南,是關於在 Open NFP[112] 平臺上用 Netronome 操作的。其它的那些,來自 Jesper 的演講,XDP 能爲其它人做什麼[113](及其第二版[114]),可能是 XDP 入門的最好的方法之一。

示例

有示例是非常好的。看看它們是如何工作的。但是 BPF 程序示例是分散在幾個項目中的,因此,我列出了我所知道的所有的示例。示例並不是總是使用相同的 helper(例如,tc 和 bcc 都有一套它們自己的 helper,使它可以很容易地去用 C 語言寫 BPF 程序)

來自內核的示例

內核中包含了大多數類型的程序:過濾器綁定到套接字或者 tc 接口、事件跟蹤/監視、甚至是 XDP。你可以在 linux/samples/bpf/[115] 目錄中找到這些示例。

現在,更多的示例已經作爲單元測試被添加到 linux/tools/testing/selftests/bpf[116] 目錄下,這裏麪包含對硬件卸載的測試或者對於 libbpf 的測試。

Jesper 的 Dangaard Brouer 在他的 prototype-kernel[117] 倉庫中也維護了一套專門的示例。 這些示例與那些內核中提供的示例非常類似,但是它們可以脫離內核架構(Makefile 和頭文件)編譯。

也不要忘記去看一下 git 相關的提交歷史,它們介紹了一些特定的特性,也許包含了一些特性的詳細示例。

來自包 iproute2 的示例

iproute2 包也提供了幾個示例。它們都很明顯地偏向網絡編程,因此,這個程序是附着到 tc 入站或者出站接口上。這些示例在 iproute2/examples/bpf/[118] 目錄中。

來自 bcc 工具集的示例

許多示例都是 與 bcc 一起提供的[119]:

  • 一些網絡的示例放在相關的目錄下面。它們包括套接字過濾器、tc 過濾器、和一個 XDP 程序。
  • tracing 目錄包含許多 跟蹤編程 的示例。前面的教程中提到的都在那裏。那些程序涉及了很大部分的事件監視功能,並且,它們中的一些是面向生產系統的。注意,某些 Linux 發行版(至少是 Debian、Ubuntu、Fedora、Arch Linux)、這些程序已經被 打包了[120] 並且可以很 “容易地” 通過比如 # apt install bcc-tools 進行安裝。但是在寫這篇文章的時候(除了 Arch Linux),首先要求安裝 IO Visor 的包倉庫。
  • 也有一些 使用 Lua 作爲一個不同的 BPF 後端(那是因爲 BPF 程序是用 Lua 寫的,它是 C 語言的一個子集,它允許爲前端和後端使用相同的語言)的一些示例,它在第三個目錄中。
  • 當然,bcc 工具[121] 自身就是 eBPF 程序使用案例的值得關注示例。

手冊頁面

雖然 bcc 一般很容易在內核中去注入和運行一個 BPF 程序,將程序附着到 tc 接口也能通過 tc 工具自己完成。因此,如果你打算將 BPF 與 tc 一起使用,你可以在 tc-bpf(8) 手冊頁面[122] 中找到一些調用示例。

代碼

有時候,BPF 文檔或者示例並不夠,而且你只想在你喜歡的文本編輯器(它當然應該是 Vim)中去顯示代碼並去閱讀它。或者,你可能想深入到代碼中去做一個補丁程序或者爲機器增加一些新特性。因此,這裏對有關的文件的幾個建議,找到你想要的函數只取決於你自己!

在內核中的 BPF 代碼

  • 文件 linux/include/linux/bpf.h[123] 及其相對的 linux/include/uapi/bpf.h[124] 包含有關 eBPF 的 定義,它們分別用在內核中和用戶空間程序的接口。
  • 相同的方式,文件 linux/include/linux/filter.h[125] 和 linux/include/uapi/filter.h[126] 包含了用於 運行 BPF 程序 的信息。
  • BPF 相關的 主要的代碼片斷 在 linux/kernel/bpf/[127] 目錄下面。系統調用的不同操作許可,比如,程序加載或者映射管理是在文件 syscall.c 中實現,而 core.c 包含了 解析器。其它文件的命名顯而易見:verifier.c 包含 校驗器(不是開玩笑的),arraymap.c 的代碼用於與數組類型的 映射 交互,等等。
  • 有幾個與網絡(及 tc、XDP )相關的函數和 helpers 是用戶可用,其實現在 linux/net/core/filter.c[128] 中。它也包含了移植 cBPF 字節碼到 eBPF 的代碼(因爲在運行之前,內核中的所有的 cBPF 程序被轉換成 eBPF)。
  • 相關於 事件跟蹤 的函數和 helpers 都在 linux/kernel/trace/bpf_trace.c[129] 中。
  • JIT 編譯器 在它們各自的架構目錄下面,比如,x86 架構的在 linux/arch/x86/net/bpfjitcomp.c[130] 中。例外是用於硬件卸載的 JIT 編譯器,它們放在它們的驅動程序下,例如 Netronome NFP 網卡的就放在 linux/drivers/net/ethernet/netronome/nfp/bpf/jit.c[131] 。
  • 在 linux/net/sched/[132] 目錄下,你可以找到 tc 的 BPF 組件 相關的代碼,尤其是在文件 act_bpf.c (action)和 cls_bpf.c(filter)中。
  • 我並沒有在 BPF 上深入到 事件跟蹤 中,因此,我並不真正瞭解這些程序的鉤子。在 linux/kernel/trace/bpf_trace.c[133] 那裏有一些東西。如果你對它感 興趣,並且想去了解更多,你可以在 Brendan Gregg 的演示或者博客文章上去深入挖掘。
  • 我也沒有使用過 seccomp-BPF,不過你能在 linux/kernel/seccomp.c[134] 找到它的代碼,並且可以在 linux/tools/testing/selftests/seccomp/seccomp_bpf.c[135] 中找到一些它的使用示例。

XDP 鉤子代碼

一旦裝載進內核的 BPF 虛擬機,由一個 Netlink 命令將 XDP 程序從用戶空間鉤入到內核網絡路徑中。接收它的是在 linux/net/core/dev.c[136] 文件中的 dev_change_xdp_fd() 函數,它被調用並設置一個 XDP 鉤子。鉤子被放在支持的網卡的驅動程序中。例如,用於 Netronome 硬件鉤子的 ntp 驅動程序實現放在 drivers/net/ethernet/netronome/nfp/[137] 中。文件 nfp_net_common.c接受 Netlink 命令,並調用 nfp_net_xdp_setup(),它會轉而調用 nfp_net_xdp_setup_drv() 實例來安裝該程序。

在 bcc 中的 BPF 邏輯

在 bcc 的 GitHub 倉庫[138] 能找到的 bcc 工具集的代碼。其 Python 代碼,包含在 BPF 類中,最初它在文件 bcc/src/python/bcc/__init__.py[139] 中。但是許多我覺得有意思的東西,比如,加載 BPF 程序到內核中,出現在 libbcc 的 C 庫[140]中。

使用 tc 去管理 BPF 的代碼

當然,這些代碼與 iproute2 包中的 tc 中的 BPF 相關。其中的一些在 iproute2/tc/[141] 目錄中。文件 f_bpf.c 和 m_bpf.c(和 e_bpf.c)各自用於處理 BPF 的過濾器和動作的(和 tc exec 命令,等等)。文件 q_clsact.c 定義了爲 BPF 特別創建的 clsactqdisc。但是,大多數的 BPF 用戶空間邏輯 是在 iproute2/lib/bpf.c[142] 庫中實現的,因此,如果你想去使用 BPF 和 tc,這裏可能是會將你搞混亂的地方(它是從文件 iproute2/tc/tc_bpf.c 中移動而來的,你也可以在舊版本的包中找到相同的代碼)。

BPF 實用工具

內核中也帶有 BPF 相關的三個工具的源代碼(bpf_asm.c、 bpf_dbg.c、 bpf_jit_disasm.c),根據你的版本不同,在 linux/tools/net/[143] (直到 Linux 4.14)或者 linux/tools/bpf/[144] 目錄下面:

  • bpf_asm 是一個極小的 cBPF 彙編程序。
  • bpf_dbg 是一個很小的 cBPF 程序調試器。
  • bpf_jit_disasm 對於兩種 BPF 都是通用的,並且對於 JIT 調試來說非常有用。
  • bpftool 是由 Jakub Kicinski 寫的通用工具,它可以與 eBPF 程序交互並從用戶空間的映射,例如,去展示、轉儲、pin 程序、或者去展示、創建、pin、更新、刪除映射。

閱讀在源文件頂部的註釋可以得到一個它們使用方法的概述。

與 eBPF 一起工作的其它必需的文件是來自內核樹的兩個用戶空間庫,它們可以用於管理 eBPF 程序或者映射來自外部的程序。這個函數可以通過 linux/tools/lib/bpf/[145] 目錄中的頭文件 bpf.h 和 libbpf.h(更高層面封裝)來訪問。比如,工具 bpftool 主要依賴這些庫。

其它值得關注的部分

如果你對關於 BPF 的不常見的語言的使用感興趣,bcc 包含 一個 BPF 目標的 P4 編譯器[146]以及 一個 Lua 前端[147],它可以被用以代替 C 的一個子集,並且(用 Lua )替代 Python 工具。

LLVM 後端

這個 BPF 後端用於 clang / LLVM 將 C 編譯到 eBPF ,是在 這個提交[148] 中添加到 LLVM 源代碼的(也可以在 這個 GitHub 鏡像[149]上訪問)。

在用戶空間中運行

到目前爲止,我知道那裏有至少兩種 eBPF 用戶空間實現。第一個是 uBPF[150],它是用 C 寫的。它包含一個解析器、一個 x86_64 架構的 JIT 編譯器、一個彙編器和一個反彙編器。

uBPF 的代碼似乎被重用來產生了一個 通用實現[151],其聲稱支持 FreeBSD 內核、FreeBSD 用戶空間、Linux 內核、Linux 用戶空間和 Mac OSX 用戶空間。它被 VALE 交換機的 BPF 擴展模塊[152]使用。

其它用戶空間的實現是我做的:rbpf[153],基於 uBPF,但是用 Rust 寫的。寫了解析器和 JIT 編譯器 (Linux 下兩個都有,Mac OSX 和 Windows 下僅有解析器),以後可能會有更多。

提交日誌

正如前面所說的,如果你希望得到更多的關於一些特定的 BPF 特性的信息,不要猶豫,去看一些提交日誌。你可以在許多地方搜索日誌,比如,在 git.kernel.org[154]、在 GitHub 上[155]、或者如果你克隆過它還有你的本地倉庫中。如果你不熟悉 git,你可以嘗試像這些去做 git blame <file> 去看看介紹特定代碼行的提交內容,然後,git show <commit> 去看詳細情況(或者在 git log 的結果中按關鍵字搜索,但是這樣做通常比較單調乏味)也可以看在 bcc 倉庫中的 按內核版本區分的 eBPF 特性列表[156],它鏈接到相關的提交上。

排錯

對 eBPF 的追捧是最近的事情,因此,到目前爲止我還找不到許多關於怎麼去排錯的資源。所以這裏只有幾個,是我在使用 BPF 進行工作的時候,對自己遇到的問題進行的記錄。

編譯時的錯誤

  • 確保你有一個最新的 Linux 內核版本(也可以看 這個文檔[157])。
  • 如果你自己編譯內核:確保你安裝了所有正確的組件,包括內核鏡像、頭文件和 libc。
  • 當使用 tc-bpf(用於去編譯 C 代碼到 BPF 中)的 man 頁面提供的 bcc shell 函數時:我曾經必須添加包含 clang 調用的頭文件:
    __bcc() {
            clang -O2 -I "/usr/src/linux-headers-$(uname -r)/include/" \
                      -I "/usr/src/linux-headers-$(uname -r)/arch/x86/include/" \
                    -emit-llvm -c $1 -o - | \
            llc -march=bpf -filetype=obj -o "`basename $1 .c`.o"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (現在似乎修復了)。

  • 對於使用 bcc 的其它問題,不要忘了去看一看這個工具集的 答疑[158]。
  • 如果你從一個並不精確匹配你的內核版本的 iproute2 包中下載了示例,可能會由於在文件中包含的頭文件觸發一些錯誤。這些示例片斷都假設安裝在你的系統中內核的頭文件與 iproute2 包是相同版本的。如果不是這種情況,下載正確的 iproute2 版本,或者編輯示例中包含的文件的路徑,指向到 iproute2 中包含的頭文件上(在運行時一些問題可能或者不可能發生,取決於你使用的特性)。

在加載和運行時的錯誤

  • 使用 tc 去加載一個程序,確保你使用了一個與使用中的內核版本等價的 iproute2 中的 tc 二進制文件。
  • 使用 bcc 去加載一個程序,確保在你的系統上安裝了 bcc(僅下載源代碼去運行 Python 腳本是不夠的)。
  • 使用 tc,如果 BPF 程序不能返回一個預期值,檢查調用它的方式:過濾器,或者動作,或者使用 “直傳” 模式的過濾器。
  • 還是 tc,注意不使用過濾器,動作不會直接附着到 qdiscs 或者接口。
  • 通過內核校驗器拋出錯誤到解析器可能很難。內核文檔[159]或許可以提供幫助,因此,可以 參考指南[160] 或者,萬不得一的情況下,可以去看源代碼(祝你好運!)。記住,校驗器 不運行 程序,對於這種類型的錯誤,記住這點是非常重要的。如果你得到一個關於無效內存訪問或者關於未初始化的數據的錯誤,它並不意味着那些問題真實發生了(或者有時候是,它們完全有可能發生)。它意味着你的程序是以校驗器預計可能發生錯誤的方式寫的,並且因此而拒絕這個程序。
  • 注意 tc 工具有一個 verbose 模式,它與 BPF 一起工作的很好:在你的命令行尾部嘗試追加一個 verbose
  • bcc 也有一個 verbose 選項:BPF 類有一個 debug 參數,它可以帶 DEBUG_LLVM_IRDEBUG_BPF 和 DEBUG_PREPROCESSOR 三個標誌中任何組合(詳細情況在 源文件[161]中)。 爲調試該代碼,它甚至嵌入了 一些條件去打印輸出代碼[162]。
  • LLVM v4.0+ 爲 eBPF 程序 嵌入一個反彙編器[163]。因此,如果你用 clang 編譯你的程序,在編譯時添加 -g 標誌允許你通過內核校驗器去以人類可讀的格式去轉儲你的程序。處理轉儲文件,使用:
    $ llvm-objdump -S -no-show-raw-insn bpf_program.o
    
    • 1
  • 使用映射?你應該去看看 bpf-map[164],這是一個爲 Cilium 項目而用 Go 創建的非常有用的工具,它可以用於去轉儲內核中 eBPF 映射的內容。也有一個用 Rust 開發的 克隆[165]。
  • 在 StackOverflow 上有個舊的 bpf 標籤[166],但是,在這篇文章中從沒用過它(並且那裏幾乎沒有與新版本的 eBPF 相關的東西)。如果你是一位來自未來的閱讀者,你可能想去看看在這方面是否有更多的活動(LCTT 譯註:意即只有舊東西)。

更多!

請經常會回到這篇博客[173]中,來看一看 關於 BPF[174] 有沒有新的文章!

特別感謝 Daniel Borkmann 指引我找到了 更多的文檔[175],因此我才完成了這個合集。


via: https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/

作者:Quentin Monnet[176] 譯者:qhwdw[177] 校對:wxy[178]

本文由 LCTT[179] 原創編譯,Linux中國[180] 榮譽推出


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章