無(wú)論好壞,C語(yǔ)言已經(jīng)是內(nèi)核開(kāi)發(fā)領(lǐng)域的通用語(yǔ)言了。Linux 內(nèi)核的核心邏輯完全是用 C 語(yǔ)言編寫(xiě)的(加上一點(diǎn)匯編),它的驅(qū)動(dòng)程序和 module 也是如此。雖然 C 語(yǔ)言因其強(qiáng)大而簡(jiǎn)單的語(yǔ)義而受到贊譽(yù),但它是一種古老的語(yǔ)言,缺乏現(xiàn)代語(yǔ)言(如 Rust)中的許多特性。另一方面,BPF 子系統(tǒng)也提供了一個(gè)編程環(huán)境,工程師能夠編寫(xiě)可以在內(nèi)核空間安全運(yùn)行的程序。在愛(ài)爾蘭都柏林舉行的 2022 年 Linux Plumbers Conference 上,Alexei Starovoitov 概述了 BPF 多年來(lái)的發(fā)展,為內(nèi)核編程提供了一個(gè)新的參考模型。
BPF的使命
Starovoitov 首先描述了他對(duì) BPF 的 "mission statement, 使命宣言":"創(chuàng)新、并啟發(fā)大家創(chuàng)新"。內(nèi)核中的編程歷來(lái)是在兩種情況下進(jìn)行的:
core kernel 開(kāi)發(fā),包括主要的核心子系統(tǒng),如內(nèi)存管理、調(diào)度器、read-copy-update,等等。
kernel-module 開(kāi)發(fā),指的是構(gòu)建那些不被編譯到 main kernel image 里的內(nèi)容,由 module loader 在后續(xù)加載。例如,驅(qū)動(dòng)程序被寫(xiě)成一些內(nèi)核 module,也有其他功能是這么做的,如文件系統(tǒng)、網(wǎng)絡(luò)協(xié)議等等。
這是內(nèi)核在很長(zhǎng)一段時(shí)間內(nèi)的狀態(tài),直到 3.15 版的內(nèi)核中加入了最早版本的 extended BPF(eBPF)虛擬機(jī)。有了它之后,BPF program 可以用一個(gè)受到嚴(yán)格限制的 C 語(yǔ)言來(lái)編寫(xiě),并被編譯成 BPF 字節(jié)碼,這將允許用戶(hù)編寫(xiě)的代碼可以經(jīng)過(guò)驗(yàn)證確保安全,然后再在內(nèi)核空間運(yùn)行。
從那時(shí)起,BPF 在代碼的規(guī)模、用戶(hù)及貢獻(xiàn)者社區(qū)的規(guī)模方面都穩(wěn)步增長(zhǎng)。根據(jù) Starovoitov 的說(shuō)法,BPF 郵件列表上每天都會(huì)收到 50-70 條信息,每月大約收到 2000 封郵件。平均每月里活躍貢獻(xiàn)的 BPF 貢獻(xiàn)者的數(shù)量也在同步增長(zhǎng),截至 2022 年 9 月,已達(dá)到約 140 人。目前來(lái)說(shuō),對(duì) BPF 子系統(tǒng)的大部分貢獻(xiàn)都不是來(lái)自 Meta BPF 小組了。
BPF編程環(huán)境
雖然大多數(shù) BPF 程序是用 C 語(yǔ)言編寫(xiě)的,并用 LLVM Clang 編譯器編譯,但 BPF program 只是二進(jìn)制 BPF 字節(jié)碼對(duì)象文件,并未規(guī)定要用某種特定的語(yǔ)言來(lái)寫(xiě)。比如說(shuō),BPF 程序可以使用 Aya 來(lái)采用 Rust 編寫(xiě),甚至可以直接用 BPF 匯編語(yǔ)言編寫(xiě)。也就是說(shuō),C是 BPF 程序的典型(canonical)編程語(yǔ)言;Starovoitov 的演講繼續(xù)概述了 BPF program 開(kāi)發(fā)中 C 編程環(huán)境是如何演進(jìn)的。
這個(gè)新的編程環(huán)境混合使用了 C 語(yǔ)言擴(kuò)展以及運(yùn)行時(shí)環(huán)境的組合實(shí)現(xiàn)的,這個(gè)運(yùn)行時(shí)環(huán)境包含了 Clang、用戶(hù)空間的 BPF 加載器庫(kù)(libbpf)和內(nèi)核中的 BPF 子系統(tǒng)。要想創(chuàng)建一個(gè) BPF 程序,用戶(hù)只要用 C 語(yǔ)言寫(xiě)一個(gè)程序,由 Clang 的 backend 實(shí)現(xiàn)來(lái)轉(zhuǎn)換成 BPF 指令。在運(yùn)行程序時(shí),libbpf 將 BPF 程序加載到內(nèi)存中,對(duì)程序進(jìn)行重定位以使其可以跨平臺(tái)以及不同的內(nèi)核版本從而具備良好的可移植性,然后調(diào)用 kernel 來(lái)加載程序。最后在內(nèi)核中,verifier 會(huì)采用靜態(tài)方式驗(yàn)證該程序是否可以安全運(yùn)行,然后啟用之。
然而,BPF 的編程環(huán)境并不是一上來(lái)就這么豐富的。在 BPF 的早期,程序被要求使用 Starovoitov 所說(shuō)的 "restricted C"。BPF 程序中的所有函數(shù)都必須完全是 inline 的,loop 循環(huán)、靜態(tài)變量和全局變量以及內(nèi)存分配都是不允許的。也沒(méi)有類(lèi)型信息(type information),所以 BPF 程序只能接收單一的、固定的 input context,用于 tracing 以及 network-filtering 相關(guān)功能。
盡管在這樣一個(gè)高度限制性的環(huán)境中編寫(xiě) BPF 程序也是很有用的,但很明顯, BPF 所支持的使用場(chǎng)景還可以得到很大的擴(kuò)展。其中一個(gè)擴(kuò)展就是允許在 BPF 程序中使用靜態(tài)函數(shù)。這樣做需要使用 libbpf 在程序加載時(shí)對(duì)內(nèi)核 BPF 程序進(jìn)行重定位。經(jīng)過(guò)多年的設(shè)計(jì)和嘗試,最終也增加了對(duì)有限循環(huán)的支持,此外也支持了 iterator。
Extending the programming environment past full C
雖然這些使得 BPF 更接近于完整的 C 語(yǔ)言了,但最終可以看到,BPF 程序需要的一些功能甚至在完整的 C 語(yǔ)言標(biāo)準(zhǔn)中都沒(méi)有。于是 BPF 社區(qū)開(kāi)始擴(kuò)展 BPF 編程環(huán)境,從而包括一些傳統(tǒng) C 語(yǔ)言沒(méi)有的新特性。其中一個(gè)擴(kuò)展功能就是 "一次編譯-到處運(yùn)行"(CO-RE, Compile Once - Run Everywhere)。
CO-RE 使 BPF 程序可以在不同的內(nèi)核版本和平臺(tái)上都可以運(yùn)行。在 BPF 程序中,訪(fǎng)問(wèn)內(nèi)核數(shù)據(jù)結(jié)構(gòu)是很常見(jiàn)的行為。然而,內(nèi)核沒(méi)有為 struct layer 確保 ABI 不變,因此,如果內(nèi)核結(jié)構(gòu)在未來(lái)的版本或不同的 config 下發(fā)生了變化,在固定偏移的地方對(duì)內(nèi)核結(jié)構(gòu)進(jìn)行讀取的 BPF 程序可能就會(huì)讀到錯(cuò)誤的值。CO-RE 通過(guò)利用運(yùn)行中的內(nèi)核中的 BPF 類(lèi)型格式(BTF)數(shù)據(jù)來(lái)解決這個(gè)問(wèn)題。在加載一個(gè)程序時(shí),libbpf 對(duì)所有的 struct 的訪(fǎng)問(wèn)都會(huì)進(jìn)行重定位,以便根據(jù)當(dāng)前運(yùn)行的內(nèi)核的 BTF 信息讓被訪(fǎng)問(wèn)的字段的偏移量匹配上。
Starovoitov 還描述了 BPF 編程環(huán)境的其他一些有趣的新增功能。其中一個(gè)是 kptrs,它允許將內(nèi)核內(nèi)存的指針存儲(chǔ)在 BPF map 中。另一個(gè)功能是允許程序在加載時(shí)訪(fǎng)問(wèn)內(nèi)核 config 參數(shù)。內(nèi)核 module 只能使用編譯時(shí)設(shè)置的 config 值,但 BPF 程序在加載時(shí)可以根據(jù)當(dāng)前內(nèi)核的配置來(lái)決定自己的行為。還有一個(gè)特點(diǎn)是 "type tags",可以讓程序能對(duì)變量進(jìn)行 annotation,從而描述它們的使用方式。例如,kptrs 可以用 __kptr 和 __kptr_ref type tags 來(lái)進(jìn)行標(biāo)注,從而表明它們分別是 unreferenced 或者 referenced kptr。當(dāng)然指針也可以用 __user 或 __percpu 標(biāo)準(zhǔn),來(lái)告訴編譯器和 verifier 這個(gè)指針?lè)謩e指向用戶(hù)內(nèi)存或 per-CPU 內(nèi)存。
Plans for the future
目前正在設(shè)計(jì)和實(shí)現(xiàn)更多的擴(kuò)展,包括 lock-correctness 正確性驗(yàn)證,以及支持 BPF 程序包含 assertion。lock 的驗(yàn)證乍一看似乎是一個(gè)很難解決的問(wèn)題,而 Dave Marchevsky 和 Kumar Kartikeya Dwivedi 都已經(jīng)發(fā)出了 RFC patch set 來(lái)實(shí)現(xiàn)用于 lock 驗(yàn)證的新 map type。Marchevsky 的 patch set 提出了一個(gè)新的紅黑樹(shù) map type,而 Dwivedi 的 patch set 提出了一個(gè) list map type。這兩個(gè) patch set 都實(shí)現(xiàn)了共同的效果,允許 BPF 程序執(zhí)行由 verifier 檢查和驗(yàn)證過(guò)的 locking 機(jī)制。
assertion 驗(yàn)證仍處于規(guī)劃階段,實(shí)現(xiàn)起來(lái)可能會(huì)很復(fù)雜。assertion 將作為給編譯器和 verifier 的信號(hào),assertion 被用來(lái)指示程序中的一些不變的因素,這些不變因素的失敗將導(dǎo)致程序中止。Starovoitov 聲稱(chēng),弄清如何讓程序中止,這會(huì)是一個(gè) "有趣" 的問(wèn)題,因?yàn)樗枰踩貙?duì)堆棧進(jìn)行 unwind,調(diào)用 kptr destructor,以及其他收尾工作。
Starovoitov 在演講的最后分享了他對(duì) BPF 未來(lái)的觀點(diǎn):會(huì)取代內(nèi)核模塊成為擴(kuò)展內(nèi)核的有效方式。早期版本的 BPF 程序看起來(lái)更像是帶有固定的 BPF helper function 和固定的 map type 的用戶(hù)空間程序,而如今新的 BPF 已經(jīng)可以讓用戶(hù)在更多個(gè)性化使用場(chǎng)景下對(duì)內(nèi)核進(jìn)行擴(kuò)展。事實(shí)上,這樣的使用場(chǎng)景已經(jīng)在 upstream 社區(qū)被提出來(lái)了。在 Starovoitov 之后在 LPC 發(fā)言的 Benjamin Tissoires,一直在開(kāi)發(fā)一個(gè) patch set,希望用 BPF 程序來(lái) fix 人類(lèi)輸入設(shè)備(HID)的 quirk。到目前為止,還沒(méi)有一個(gè)內(nèi)核 module 被 BPF 程序完全取代掉,不過(guò),很期待看到內(nèi)核的其他一些功能可以在 BPF 程序中實(shí)現(xiàn)。
一位聽(tīng)眾要求了解 Starovoitov 所提到的 lock-correctness 驗(yàn)證的更多細(xì)節(jié)。Starovoitov 說(shuō),這個(gè)工作還在進(jìn)行當(dāng)中,但他樂(lè)觀地認(rèn)為可以找到一種方法來(lái)進(jìn)行 static lock checking,從而驗(yàn)證數(shù)據(jù)保護(hù)是正確的,并保證不會(huì)發(fā)生死鎖。Dave Miller 回應(yīng)說(shuō),如果鎖可以由 verifier 進(jìn)行靜態(tài)檢查,那么可能可以研究一下 locking 邏輯是否可以由 verifier 自動(dòng)生成。Starovoitov 回答說(shuō),這就是他們希望實(shí)現(xiàn)的目標(biāo),目前的設(shè)計(jì)中將 lock 和受保護(hù)的數(shù)據(jù)在同一次 allocation 中放在一起。對(duì)于不能跟 lock 放在一起的數(shù)據(jù),可以用 BTF type tag 來(lái)指定它需要明確進(jìn)行鎖保護(hù)。
審核編輯 :李倩
-
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136864 -
編程
+關(guān)注
關(guān)注
88文章
3616瀏覽量
93742 -
驅(qū)動(dòng)程序
+關(guān)注
關(guān)注
19文章
836瀏覽量
48042 -
BPF
+關(guān)注
關(guān)注
0文章
25瀏覽量
4006
原文標(biāo)題:LWN:讓BPF成為一個(gè)更安全的內(nèi)核編程環(huán)境!
文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論