LCTT 翻譯:昨天,AlmaLinux 稱將 放棄 對RHEL 的1:1 相容性,但將保持對RHEL 的ABI 相容,以便在RHEL 上執行的軟體可以無縫地運行在AlmaLinux 上。可能有的同學對 ABI 的概念還不是很清楚,因此翻譯此文供大家了解。
許多Linux 愛好者都熟悉Linus Torvalds 的 著名告誡:“我們不破壞用戶空間”,但可能並非每個聽到這句話的人都清楚其含義。
這個「第一規則」提醒開發人員關於應用程式的二進位介面(ABI)的穩定性,該介面用於應用程式與核心之間的通訊和配置。接下來的內容旨在使讀者熟悉 ABI 的概念,闡述為什麼 ABI 的穩定性很重要,並討論 Linux 穩定 ABI 中包含了哪些內容。 Linux 的持續成長和演進需要對 ABI 進行變更,其中一些變更引起了爭議。
ABI 表示 應用程式二進位介面Applications Binary Interface。理解 ABI 概念的一種方式是考慮它與其他概念的差異。對許多開發人員來說,應用程式介面Applications Programming Interface(API)更為熟悉。通常,庫的頭文件和文件被認為是其 API,以及還有像 HTML5 這樣的標準文件。呼叫庫或交換字串格式資料的程式必須遵守 API 中所描述的約定,否則可能會得到意外的結果。
ABI 類似於 API,因為它們規定了命令的解釋和二進位資料的交換方式。對於 C 程序,ABI 通常包括函數的傳回類型和參數清單、結構體的佈局,以及枚舉類型的含義、順序和範圍。截至 2022 年,Linux 核心仍然幾乎完全是 C 程序,因此必須遵守這些規範。
「核心系統呼叫介面」 的描述可以在《Linux 手冊第2 節》中找到,並包含了可從中介軟體應用程式呼叫的類似mount
和 sync
的C 版本函數。這些函數的二進位佈局是 Linux ABI 的第一個重要組成部分。對於問題 “Linux 的穩定 ABI 包括哪些內容?”,許多使用者和開發人員的回答是 “sysfs(/sys
)和 procfs(/proc
)的內容”。而實際上,官方 Linux ABI 文件 確實主要集中在這些 虛擬檔案系統 。
前面著重介紹了 Linux ABI 在程式中的應用方式,但未涵蓋同等重要的人為因素。如下圖所示,ABI 的功能需要核心社群、C 編譯器(如 GCC 或 clang)、建立使用者空間C 函式庫(通常是 glibc )的開發人員,以及按照 可執行與連結格式(ELF) 佈局的二進位應用程式之間的合作努力。
開發社群內的合作
#來自 Torvalds 本人的 Linux ABI 的穩定性保證,使得 Linux 發行版和個人用戶能夠獨立更新內核,而不受作業系統的影響。
如果 Linux 沒有穩定的 ABI,那麼每次核心需要修補以解決安全問題時,作業系統的大部分甚至全部內容都需要重新安裝。顯然,二進位介面的穩定性是 Linux 的可用性和廣泛採用的重要因素之一。
Terminal output
如上圖所示,核心(在 linux-libc-dev
中)和Glibc(在 libc6-dev
中)都提供了定義檔權限的位元遮罩。顯然,這兩個定義集必須一致! apt
軟體套件管理器會辨識軟體包提供每個檔案。 Glibc ABI 的潛在不穩定部分位於 bits/
目錄中。
在大部分情況下,Linux ABI 的穩定性保證運作良好。根據 康韋定律Conway's Law,在開發過程中出現的煩人技術問題往往是由於不同軟體開發社群之間的誤解或分歧所致,而這些社群都為 Linux 做出了貢獻。不同社群之間的介面可以透過 Linux 套件管理器的元資料輕鬆地進行想像,如上圖所示。
#透過考慮目前正在進行的、緩慢發生 的「Y2038」 ABI 破壞的例子,可以更好地理解Linux ABI 。在 2038 年 1 月,32 位元時間計數器將回滾到全零,就像較舊車輛的里程表一樣。 2038 年 1 月聽起來還很遙遠,但可以肯定的是,如今銷售的許多物聯網設備仍將處於運作狀態。像今年安裝的 智慧電錶 和 智慧停車系統 這樣的普通產品可能採用的是 32 位元處理器架構,也可能不支援軟體更新。
Linux 核心已經在內部轉向使用 64 位元的 time_t
不透明資料類型來表示更晚的時間點。這意味著像 time()
這樣的系統呼叫在 64 位元系統上已經變更了它們的函數簽章。這些努力的困難程度可以在核心頭檔中(例如 time_types.h)清楚地看到,在那裡放著新的和 _old
版本的資料結構。
里程表翻轉
Glibc 專案也 支援 64 位元時間,那麼就大功告成了,對嗎?不幸的是,根據 Debian 郵件清單中的討論 來看,情況並非如此。發行版面臨難以選擇的問題,要么為 32 位元系統提供所有二進位軟體套件的兩個版本,要么為安裝媒體提供兩個版本。在後一種情況下,32 位元時間的用戶將不得不重新編譯其應用程式並重新安裝。正如往常一樣,專有應用程式才是真正的頭痛問題。
理解穩定 ABI 有些微妙。需要考慮的是,儘管大部分 sysfs 是穩定 ABI,但偵錯介面肯定是不穩定的,因為它們將核心內部暴露給使用者空間。 Linus Torvalds 曾表示,“不要破壞用戶空間”,通常情況下,他是指保護那些“只想它能工作” 的普通用戶,而不是系統程式設計師和核心工程師,後者應該能夠閱讀核心文件和來源程式碼,以了解不同版本之間發生了什麼變化。下圖展示了這個差異。
穩定性保證
一般使用者不太可能與 Linux ABI 的不穩定部分進行交互,但係統程式設計師可能無意中這樣做。除了 /sys/kernel/debug
以外,sysfs(/sys
)和 procfs(/proc
)的所有部分都是穩定的。
那麼其他對使用者空間可見的二進位介面如何呢,包括 /dev
中的裝置檔案、核心日誌檔案(可透過 dmesg
指令讀取)、檔案系統元資料或在核心的「命令列」 中提供的「引導參數」(在引導程式如GRUB 或u-boot 中可見)呢?當然,「這要視情況而定」。
除了 Linux 系統在引導過程中出現掛起之外,檔案系統無法掛載是最令人失望的事情。如果檔案系統位於付費客戶的固態硬碟上,那麼問題確實十分嚴重。當核心升級時,一個能夠在舊核心版本下掛載的 Linux 檔案系統應該仍然能夠掛載,對嗎?實際上,「這要視情況而定」。
在2020 年,一位受到傷害的Linux 開發人員在核心的郵件清單上 抱怨道:
核心已經接受這個作為一個有效的可掛載檔案系統格式,沒有任何錯誤或任何類型的警告,而且已經這樣穩定地工作了多年……我一直普遍地以為,掛載現有的根文件系統屬於內核<->用戶空間或核心<->現有系統邊界的範圍,由核心接受並被現有用戶空間成功使用的內容所定義,升級核心應該與現有用戶空間和系統相容。
但是有一個問題:這些無法掛載的檔案系統是使用一種依賴於核心定義,但並未被核心使用的標誌的專有工具創建的。該標誌未出現在 Linux 的 API 頭檔或 procfs/sysfs 中,而是一種 實作細節。因此,在用戶空間程式碼中解釋該標誌意味著依賴“未定義行為”,這是幾乎會讓每個軟體開發人員都感到戰慄的短語。當核心社群改進其內部測試並開始進行新的一致性檢查時,「man 2 mount」 系統呼叫突然開始拒絕具有專有格式的檔案系統。由於該格式的創建者明確地成為軟體開發人員,因此他未能得到核心檔案系統維護者的同情。
施工標誌寫著工作人員在樹上進行工作
/dev
目錄中的檔案格式是否保證穩定或不穩定? dmesg 指令 會從檔案 /dev/kmsg
讀取內容。 2018 年,一位開發人員 為dmesg 輸出實現了線程化,使核心能夠「在列印一系列 printk()
訊息到控制台時,不會中斷和/或被其他執行緒的並發 printk()
幹擾」。聽起來很棒!透過在 /dev/kmsg
輸出的每一行中新增執行緒 ID,實現了執行緒化。密切注意的讀者將意識到這個改變改變了 /dev/kmsg
的 ABI,這意味著解析該檔案的應用程式也需要進行相應的修改。由於許多發行版沒有編譯啟用新功能的內核,大多數使用 /bin/dmesg
的用戶可能沒有註意到這件事,但這個改動破壞了 GDB 偵錯器 讀取內核日誌的能力。
確實,敏銳的讀者會認為 GDB 的使用者運氣不佳,因為偵錯器是開發人員工具。實際上並非如此,因為需要更新以支援新的 /dev/kmsg
格式的程式碼位於核心自己的 Git 原始碼庫的 “樹內” 部分。對於一個正常的專案來說,單一程式碼庫內的程式無法協同工作就是一個明顯的錯誤,因此已經合併了一份 使GDB 能夠與線程化的/dev/kmsg 一起工作的補丁 。
BPF 是一種強大的工具,可以在運行的核心中監控甚至即時進行設定。 BPF 最初的目的是透過允許系統管理員即時從命令列修改資料包過濾器,從而支援即時網路配置。 Alexei Starovoitov 和其他人極大地擴展了 BPF,使其能夠追蹤任意核心函數。追蹤明顯是開發人員的領域,而不是普通用戶,因此它顯然不受任何 ABI 保證的約束(儘管 bpf() 系統呼叫 具有與其他系統呼叫相同的穩定性承諾)。另一方面,創建新功能的 BPF 程式為「取代核心模組成為擴展核心的事實標準手段」提供了可能性。核心模組使設備、檔案系統、加密、網路等工作正常,因此明顯是「只希望它工作」的普通用戶所依賴的設施。問題是,與大多數開源核心模組不同,BPF 程式傳統上不在核心原始碼中。
2022 年春季,一個提案 成為了焦點,該提案提議使用微型BPF 程式而不是裝置驅動程式補丁,對廣泛的人機介面裝置(如滑鼠和鍵盤)提供支持。
隨後進行了一場激烈的討論,但這個問題顯然在 Torvalds 在開源峰會上的評論 中解決:
他指出,如果你破壞了“普通(非核心開發人員)用戶使用的真實用戶空間工具”,那麼你需要修復它,無論是否使用了 eBPF。
一致意見似乎正在形成,即希望其BPF 程式在內核更新後仍能正常工作的開發人員 將需要將其提交到核心原始碼庫中尚未指定的位置。敬請關注後繼發展,以了解內核社群對於 BPF 和 ABI 穩定性將採取什麼樣的政策。
核心的 ABI 穩定性保證適用於 procfs、sysfs 和系統呼叫接口,但也存在重要的例外情況。當核心變更破壞了「樹內」程式碼或用戶空間應用程式時,通常會迅速回滾有問題的補丁。對於依賴內核實現細節的專有程式碼,儘管這些細節可以從用戶空間訪問,但它並沒有受到保護,並且在出現問題時得到的同情有限。當像 Y2038 這樣的問題無法避免 ABI 破壞時,會以盡可能謹慎和系統化的方式進行過渡。而像 BPF 程式這樣的新功能提出了關於 ABI 穩定性邊界的尚未解答的問題。
感謝 Akkana Peck、Sarah R. Newman 和 Luke S. Crawford 對早期版本資料的有益評論。
以上是十分鐘讓你了解 Linux ABI的詳細內容。更多資訊請關注PHP中文網其他相關文章!