lctt 譯注:昨天,almalinux 稱(chēng)將?放棄?對(duì) rhel 的 1:1 兼容性,但將保持對(duì) rhel 的 abi 兼容,以便在 rhel 上運(yùn)行的軟件可以無(wú)縫地運(yùn)行在 almalinux 上??赡苡械耐瑢W(xué)對(duì) abi 的概念還不是很清楚,因此翻譯此文供大家了解。
許多 Linux 愛(ài)好者都熟悉 Linus Torvalds 的?放棄:“我們不破壞用戶(hù)空間”,但可能并非每個(gè)聽(tīng)到這句話的人都清楚其含義。
這個(gè)“第一規(guī)則”提醒開(kāi)發(fā)人員關(guān)于應(yīng)用程序的二進(jìn)制接口(ABI)的穩(wěn)定性,該接口用于應(yīng)用程序與內(nèi)核之間的通信和配置。接下來(lái)的內(nèi)容旨在使讀者熟悉 ABI 的概念,闡述為什么 ABI 的穩(wěn)定性很重要,并討論 Linux 穩(wěn)定 ABI 中包含了哪些內(nèi)容。Linux 的持續(xù)增長(zhǎng)和演進(jìn)需要對(duì) ABI 進(jìn)行變更,其中一些變更引起了爭(zhēng)議。
什么是 ABI?
ABI 表示?應(yīng)用程序二進(jìn)制接口Applications Binary Interface。理解 ABI 概念的一種方式是考慮它與其他概念的區(qū)別。對(duì)于許多開(kāi)發(fā)人員來(lái)說(shuō),應(yīng)用程序編程接口Applications Programming Interface(API)更為熟悉。通常,庫(kù)的頭文件和文檔被認(rèn)為是其 API,以及還有像?放棄?這樣的標(biāo)準(zhǔn)文檔。調(diào)用庫(kù)或交換字符串格式數(shù)據(jù)的程序必須遵守 API 中所描述的約定,否則可能得到意外的結(jié)果。
ABI 類(lèi)似于 API,因?yàn)樗鼈円?guī)定了命令的解釋和二進(jìn)制數(shù)據(jù)的交換方式。對(duì)于 C 程序,ABI 通常包括函數(shù)的返回類(lèi)型和參數(shù)列表、結(jié)構(gòu)體的布局,以及枚舉類(lèi)型的含義、順序和范圍。截至 2022 年,Linux 內(nèi)核仍然幾乎完全是 C 程序,因此必須遵守這些規(guī)范。
“放棄” 的描述可以在《放棄》中找到,并包括了可從中間件應(yīng)用程序調(diào)用的類(lèi)似?mount?和?sync?的 C 版本函數(shù)。這些函數(shù)的二進(jìn)制布局是 Linux ABI 的第一個(gè)重要組成部分。對(duì)于問(wèn)題 “Linux 的穩(wěn)定 ABI 包括哪些內(nèi)容?”,許多用戶(hù)和開(kāi)發(fā)人員的回答是 “sysfs(/sys)和 procfs(/proc)的內(nèi)容”。而實(shí)際上,放棄?確實(shí)主要集中在這些?放棄?上。
前面著重介紹了 Linux ABI 在程序中的應(yīng)用方式,但未涵蓋同等重要的人為因素。正如下圖所示,ABI 的功能需要內(nèi)核社區(qū)、C 編譯器(如?放棄?或?放棄)、創(chuàng)建用戶(hù)空間 C 庫(kù)(通常是?放棄)的開(kāi)發(fā)人員,以及按照?放棄?布局的二進(jìn)制應(yīng)用程序之間的合作努力。
開(kāi)發(fā)社區(qū)內(nèi)的合作
為什么我們關(guān)注 ABI?
來(lái)自 Torvalds 本人的 Linux ABI 的穩(wěn)定性保證,使得 Linux 發(fā)行版和個(gè)人用戶(hù)能夠獨(dú)立更新內(nèi)核,而不受操作系統(tǒng)的影響。
如果 Linux 沒(méi)有穩(wěn)定的 ABI,那么每次內(nèi)核需要修補(bǔ)以解決安全問(wèn)題時(shí),操作系統(tǒng)的大部分甚至全部?jī)?nèi)容都需要重新安裝。顯然,二進(jìn)制接口的穩(wěn)定性是 Linux 的可用性和廣泛采用的重要因素之一。
Terminal output
如上圖所示,內(nèi)核(在?linux-libc-dev?中)和 Glibc(在?libc6-dev?中)都提供了定義文件權(quán)限的位掩碼。顯然,這兩個(gè)定義集必須一致!apt?軟件包管理器會(huì)識(shí)別軟件包提供每個(gè)文件。Glibc ABI 的潛在不穩(wěn)定部分位于?bits/?目錄中。
在大部分情況下,Linux ABI 的穩(wěn)定性保證運(yùn)作良好。按照?放棄Conway’s Law,在開(kāi)發(fā)過(guò)程中出現(xiàn)的煩人技術(shù)問(wèn)題往往是由于不同軟件開(kāi)發(fā)社區(qū)之間的誤解或分歧所致,而這些社區(qū)都為 Linux 做出了貢獻(xiàn)。不同社區(qū)之間的接口可以通過(guò) Linux 包管理器的元數(shù)據(jù)輕松地進(jìn)行想象,如上圖所示。
Y2038:一個(gè) ABI 破壞的例子
通過(guò)考慮當(dāng)前正在進(jìn)行的、放棄?的 “Y2038” ABI 破壞的例子,可以更好地理解 Linux ABI。在 2038 年 1 月,32 位時(shí)間計(jì)數(shù)器將回滾到全零,就像較舊車(chē)輛的里程表一樣。2038 年 1 月聽(tīng)起來(lái)還很遙遠(yuǎn),但可以肯定的是,如今銷(xiāo)售的許多物聯(lián)網(wǎng)設(shè)備仍將處于運(yùn)行狀態(tài)。像今年安裝的?放棄?和?放棄?這樣的普通產(chǎn)品可能采用的是 32 位處理器架構(gòu),而且也可能不支持軟件更新。
Linux 內(nèi)核已經(jīng)在內(nèi)部轉(zhuǎn)向使用 64 位的?time_t?不透明數(shù)據(jù)類(lèi)型來(lái)表示更晚的時(shí)間點(diǎn)。這意味著像?time()?這樣的系統(tǒng)調(diào)用在 64 位系統(tǒng)上已經(jīng)變更了它們的函數(shù)簽名。這些努力的艱難程度可以在內(nèi)核頭文件中(例如?放棄)清楚地看到,在那里放著新的和?_old?版本的數(shù)據(jù)結(jié)構(gòu)。
里程表翻轉(zhuǎn)
Glibc 項(xiàng)目也?放棄,那么就大功告成了,對(duì)嗎?不幸的是,根據(jù)?放棄?來(lái)看,情況并非如此。發(fā)行版面臨難以選擇的問(wèn)題,要么為 32 位系統(tǒng)提供所有二進(jìn)制軟件包的兩個(gè)版本,要么為安裝介質(zhì)提供兩個(gè)版本。在后一種情況下,32 位時(shí)間的用戶(hù)將不得不重新編譯其應(yīng)用程序并重新安裝。正如往常一樣,專(zhuān)有應(yīng)用程序才是一個(gè)真正的頭疼問(wèn)題。
Linux 穩(wěn)定 ABI 里到底包括什么內(nèi)容?
理解穩(wěn)定 ABI 有些微妙。需要考慮的是,盡管大部分 sysfs 是穩(wěn)定 ABI,但調(diào)試接口肯定是不穩(wěn)定的,因?yàn)樗鼈儗?nèi)核內(nèi)部暴露給用戶(hù)空間。Linus Torvalds 曾表示,“不要破壞用戶(hù)空間”,通常情況下,他是指保護(hù)那些 “只想它能工作” 的普通用戶(hù),而不是系統(tǒng)程序員和內(nèi)核工程師,后者應(yīng)該能夠閱讀內(nèi)核文檔和源代碼,以了解不同版本之間發(fā)生了什么變化。下圖展示了這個(gè)區(qū)別。
穩(wěn)定性保證
普通用戶(hù)不太可能與 Linux ABI 的不穩(wěn)定部分進(jìn)行交互,但系統(tǒng)程序員可能無(wú)意中這樣做。除了?/sys/kernel/debug?以外,sysfs(/sys)和 procfs(/proc)的所有部分都是穩(wěn)定的。
那么其他對(duì)用戶(hù)空間可見(jiàn)的二進(jìn)制接口如何呢,包括?/dev?中的設(shè)備文件、內(nèi)核日志文件(可通過(guò)?dmesg?命令讀?。?、文件系統(tǒng)元數(shù)據(jù)或在內(nèi)核的 “命令行” 中提供的 “引導(dǎo)參數(shù)”(在引導(dǎo)加載程序如 GRUB 或 u-boot 中可見(jiàn))呢?當(dāng)然,“這要視情況而定”。
掛載舊文件系統(tǒng)
除了 Linux 系統(tǒng)在引導(dǎo)過(guò)程中出現(xiàn)掛起之外,文件系統(tǒng)無(wú)法掛載是最令人失望的事情。如果文件系統(tǒng)位于付費(fèi)客戶(hù)的固態(tài)硬盤(pán)上,那么問(wèn)題確實(shí)十分嚴(yán)重。當(dāng)內(nèi)核升級(jí)時(shí),一個(gè)能夠在舊內(nèi)核版本下掛載的 Linux 文件系統(tǒng)應(yīng)該仍然能夠掛載,對(duì)嗎?實(shí)際上,“這要視情況而定”。
在 2020 年,一位受到傷害的 Linux 開(kāi)發(fā)人員在內(nèi)核的郵件列表上?放棄:
內(nèi)核已經(jīng)接受這個(gè)作為一個(gè)有效的可掛載文件系統(tǒng)格式,沒(méi)有任何錯(cuò)誤或任何類(lèi)型的警告,而且已經(jīng)這樣穩(wěn)定地工作了多年……我一直普遍地以為,掛載現(xiàn)有的根文件系統(tǒng)屬于內(nèi)核用戶(hù)空間或內(nèi)核現(xiàn)有系統(tǒng)邊界的范圍,由內(nèi)核接受并被現(xiàn)有用戶(hù)空間成功使用的內(nèi)容所定義,升級(jí)內(nèi)核應(yīng)該與現(xiàn)有用戶(hù)空間和系統(tǒng)兼容。
但是有一個(gè)問(wèn)題:這些無(wú)法掛載的文件系統(tǒng)是使用一種依賴(lài)于內(nèi)核定義,但并未被內(nèi)核使用的標(biāo)志的專(zhuān)有工具創(chuàng)建的。該標(biāo)志未出現(xiàn)在 Linux 的 API 頭文件或 procfs/sysfs 中,而是一種?放棄。因此,在用戶(hù)空間代碼中解釋該標(biāo)志意味著依賴(lài)于“放棄”,這是個(gè)幾乎會(huì)讓每個(gè)軟件開(kāi)發(fā)人員都感到戰(zhàn)栗的短語(yǔ)。當(dāng)內(nèi)核社區(qū)改進(jìn)其內(nèi)部測(cè)試并開(kāi)始進(jìn)行新的一致性檢查時(shí),“放棄” 系統(tǒng)調(diào)用突然開(kāi)始拒絕具有專(zhuān)有格式的文件系統(tǒng)。由于該格式的創(chuàng)建者明確是一位軟件開(kāi)發(fā)人員,因此他未能得到內(nèi)核文件系統(tǒng)維護(hù)者的同情。
施工標(biāo)志上寫(xiě)著工作人員在樹(shù)上進(jìn)行工作
線程化內(nèi)核的 dmesg 日志
/dev?目錄中的文件格式是否保證穩(wěn)定或不穩(wěn)定?放棄?會(huì)從文件?/dev/kmsg?中讀取內(nèi)容。2018 年,一位開(kāi)發(fā)人員?放棄,使內(nèi)核能夠“在打印一系列?printk()?消息到控制臺(tái)時(shí),不會(huì)被中斷和/或被其他線程的并發(fā)?printk()?干擾”。聽(tīng)起來(lái)很棒!通過(guò)在?/dev/kmsg?輸出的每一行添加線程 ID,實(shí)現(xiàn)了線程化。密切關(guān)注的讀者將意識(shí)到這個(gè)改動(dòng)改變了?/dev/kmsg?的 ABI,這意味著解析該文件的應(yīng)用程序也需要進(jìn)行相應(yīng)的修改。由于許多發(fā)行版沒(méi)有編譯啟用新功能的內(nèi)核,大多數(shù)使用?/bin/dmesg?的用戶(hù)可能沒(méi)有注意到這件事,但這個(gè)改動(dòng)破壞了?放棄?讀取內(nèi)核日志的能力。
確實(shí),敏銳的讀者會(huì)認(rèn)為 GDB 的用戶(hù)運(yùn)氣不佳,因?yàn)檎{(diào)試器是開(kāi)發(fā)人員工具。實(shí)際上并非如此,因?yàn)樾枰乱灾С中碌?/dev/kmsg?格式的代碼位于內(nèi)核自己的 Git 源代碼庫(kù)的 “樹(shù)內(nèi)” 部分。對(duì)于一個(gè)正常的項(xiàng)目來(lái)說(shuō),單個(gè)代碼庫(kù)內(nèi)的程序無(wú)法協(xié)同工作就是一個(gè)明顯的錯(cuò)誤,因此已經(jīng)合并了一份?放棄。
那么 BPF 程序呢?
放棄?是一種強(qiáng)大的工具,可以在運(yùn)行的內(nèi)核中監(jiān)控甚至實(shí)時(shí)進(jìn)行配置。BPF 最初的目的是通過(guò)允許系統(tǒng)管理員即時(shí)從命令行修改數(shù)據(jù)包過(guò)濾器,從而支持實(shí)時(shí)網(wǎng)絡(luò)配置。放棄,使其能夠跟蹤任意內(nèi)核函數(shù)。跟蹤明顯是開(kāi)發(fā)人員的領(lǐng)域,而不是普通用戶(hù),因此它顯然不受任何 ABI 保證的約束(盡管?放棄?具有與其他系統(tǒng)調(diào)用相同的穩(wěn)定性承諾)。另一方面,創(chuàng)建新功能的 BPF 程序?yàn)椤?a href="http://m.babyishan.com/?golink=aHR0cHM6Ly93d3cucGhwLmNuL2xpbmsvNzQxMDVkMzczYTcxYjUxN2VkNjUwY2FhYmI5YzJjYjg=" >放棄”提供了可能性。內(nèi)核模塊使設(shè)備、文件系統(tǒng)、加密、網(wǎng)絡(luò)等工作正常,因此明顯是“只希望它工作”的普通用戶(hù)所依賴(lài)的設(shè)施。問(wèn)題是,與大多數(shù)開(kāi)源內(nèi)核模塊不同,BPF 程序傳統(tǒng)上不在內(nèi)核源代碼中。
2022 年春季,放棄?成為了焦點(diǎn),該提案提議使用微型 BPF 程序而不是設(shè)備驅(qū)動(dòng)程序補(bǔ)丁,對(duì)廣泛的人機(jī)接口設(shè)備(如鼠標(biāo)和鍵盤(pán))提供支持。
隨后進(jìn)行了一場(chǎng)激烈的討論,但這個(gè)問(wèn)題顯然在?放棄?中得到解決:
他指出,如果你破壞了“普通(非內(nèi)核開(kāi)發(fā)人員)用戶(hù)使用的真實(shí)用戶(hù)空間工具”,那么你需要修復(fù)它,無(wú)論是否使用了 eBPF。
一致意見(jiàn)似乎正在形成,即希望其 BPF 程序在內(nèi)核更新后仍能正常工作的開(kāi)發(fā)人員?放棄。敬請(qǐng)關(guān)注后繼發(fā)展,以了解內(nèi)核社區(qū)對(duì)于 BPF 和 ABI 穩(wěn)定性將采取什么樣的政策。
結(jié)論
內(nèi)核的 ABI 穩(wěn)定性保證適用于 procfs、sysfs 和系統(tǒng)調(diào)用接口,但也存在重要的例外情況。當(dāng)內(nèi)核變更破壞了“樹(shù)內(nèi)”代碼或用戶(hù)空間應(yīng)用程序時(shí),通常會(huì)迅速回滾有問(wèn)題的補(bǔ)丁。對(duì)于依賴(lài)內(nèi)核實(shí)現(xiàn)細(xì)節(jié)的專(zhuān)有代碼,盡管這些細(xì)節(jié)可以從用戶(hù)空間訪問(wèn),但它并沒(méi)有受到保護(hù),并且在出現(xiàn)問(wèn)題時(shí)得到的同情有限。當(dāng)像 Y2038 這樣的問(wèn)題無(wú)法避免 ABI 破壞時(shí),會(huì)以盡可能慎重和系統(tǒng)化的方式進(jìn)行過(guò)渡。而像 BPF 程序這樣的新功能提出了關(guān)于 ABI 穩(wěn)定性邊界的尚未解答的問(wèn)題。