Linux 核心驅動介面¶
(你的所有問題都得到了解答,甚至更多)
Greg Kroah-Hartman <greg@kroah.com>
本文旨在解釋為什麼 Linux 沒有二進位制核心介面,也沒有穩定的核心介面。
注意
請注意,本文描述的是核心內部介面,而不是核心到使用者空間的介面。
核心到使用者空間的介面是應用程式使用的介面,即系統呼叫介面。該介面隨時間推移非常穩定,不會被破壞。我有一些在 0.9 版本以前的核心上構建的舊程式,它們在最新的 2.6 核心版本上仍然執行良好。使用者和應用程式程式設計師可以信賴這個介面的穩定性。
執行摘要¶
你認為你想要一個穩定的核心介面,但實際上你並不想要,你甚至都不知道。你想要的是一個穩定執行的驅動程式,只有當你的驅動程式在主核心樹中時才能實現。如果你的驅動程式在主核心樹中,你還會獲得許多其他好處,所有這些都使得 Linux 成為一個強大、穩定和成熟的作業系統,這也是你最初使用它的原因。
引言¶
只有那些想要編寫核心驅動程式的人才需要擔心核心內部介面的變化。對於世界上絕大多數人來說,他們既看不到這個介面,也根本不關心它。
首先,我不會討論任何關於閉源、隱藏原始碼、二進位制大檔案(binary blobs)、原始碼封裝或任何其他描述其原始碼未在 GPL 下發布的核心驅動程式的法律問題。如果您有任何法律問題,請諮詢律師,我是一名程式設計師,因此,我只在這裡描述技術問題(並非要輕視法律問題,它們是真實存在的,您確實需要時刻注意它們)。
所以,這裡有兩個主要議題:二進位制核心介面和穩定核心原始碼介面。它們彼此依賴,但我們先討論二進位制部分,以將其解決。
二進位制核心介面¶
假設我們有一個穩定的核心原始碼介面,那麼一個二進位制介面也會自然而然地出現,對嗎?錯了。請考慮以下關於 Linux 核心的事實
根據您使用的 C 編譯器版本,不同的核心資料結構將包含不同的結構對齊方式,並且可能以不同方式包含不同的函式(將函式內聯或不內聯)。單個函式組織並不那麼重要,但不同的資料結構填充非常重要。
根據您選擇的核心構建選項,核心可能會做出各種不同的假設
不同的結構可能包含不同的欄位
某些功能可能根本未實現(例如,對於非 SMP 構建,某些鎖會編譯為空)。
核心中的記憶體可以以不同的方式對齊,具體取決於構建選項。
Linux 執行在各種不同的處理器架構上。來自一種架構的二進位制驅動程式無法在另一種架構上正常執行。
現在,可以透過簡單地為精確特定的核心配置編譯您的模組,並使用構建核心的完全相同的 C 編譯器來解決其中一些問題。如果您想為特定 Linux 發行版的特定釋出版本提供模組,這已經足夠了。但是,將單個構建乘以不同 Linux 發行版的數量以及 Linux 發行版支援的不同釋出版本的數量,您很快就會遇到不同釋出版本上不同構建選項的噩夢。還要意識到,每個 Linux 發行版釋出都包含許多不同的核心,所有這些核心都針對不同的硬體型別(不同的處理器型別和不同的選項)進行了調整,因此即使對於單個釋出版本,您也需要建立模組的多個版本。
相信我,如果你試圖支援這種釋出方式,久而久之你會發瘋的,我很久以前就以這種艱難的方式學到了這一點……
穩定核心原始碼介面¶
如果你與那些試圖長期維護不在主核心樹中的 Linux 核心驅動程式的人交流,這是一個更“不穩定”的話題。
Linux 核心開發是持續且高速進行的,從未放慢腳步。因此,核心開發者會發現當前介面中的錯誤,或找出更好的方法來處理問題。如果他們這樣做,他們就會修復當前介面以使其更好地工作。當他們這樣做時,函式名稱可能會改變,結構可能會增大或縮小,函式引數可能會被重新調整。如果發生這種情況,核心中所有使用此介面的例項都會同時得到修復,確保一切繼續正常工作。
作為具體示例,核心內部的 USB 介面在該子系統的生命週期內至少經歷了三次不同的重構。這些重構是為了解決許多不同的問題
從資料流的同步模型更改為非同步模型。這降低了許多驅動程式的複雜性,並提高了所有 USB 驅動程式的吞吐量,使我們現在幾乎所有 USB 裝置都能以其可能的最大速度執行。
在 USB 驅動程式從 USB 核心分配資料包的方式上進行了更改,這樣所有驅動程式現在都需要向 USB 核心提供更多資訊,以修復一些已知的死鎖問題。
這與一些閉源作業系統形成鮮明對比,這些作業系統不得不長期維護其舊的 USB 介面。這使得新開發者有可能意外使用舊介面並以不正確的方式行事,從而導致作業系統的穩定性下降。
在這兩種情況下,所有開發者都同意這些都是需要進行的重要更改,並且它們是在相對較小的痛苦下完成的。如果 Linux 必須確保它將保留一個穩定的原始碼介面,那麼就會建立一個新介面,而舊的、損壞的介面將不得不長期維護,從而給 USB 開發者帶來額外的工作。由於所有 Linux USB 開發者都在自己的時間內工作,要求程式設計師免費做額外的工作而沒有收益是不可能的。
安全問題對 Linux 來說也非常重要。當發現安全問題時,它會在很短的時間內得到修復。很多次,這導致了內部核心介面的重構,以防止安全問題的發生。當這種情況發生時,所有使用這些介面的驅動程式也會同時得到修復,確保安全問題得到解決,並且將來不會意外地再次出現。如果內部介面不允許更改,那麼修復此類安全問題並確保其不再發生將是不可能的。
核心介面會隨著時間的推移進行清理。如果當前沒有人在使用某個介面,它就會被刪除。這確保了核心儘可能小,並且所有潛在的介面都得到了儘可能好的測試(未使用的介面幾乎不可能測試其有效性)。
該怎麼辦¶
那麼,如果你有一個不在主核心樹中的 Linux 核心驅動程式,你作為一個開發者該怎麼辦?為每個不同發行版的每個不同核心版本釋出二進位制驅動程式是一個噩夢,而且試圖跟上不斷變化的核心介面也是一項艱鉅的工作。
很簡單,將你的核心驅動程式納入主核心樹(請記住,我們這裡談論的是在 GPL 相容許可下發布的驅動程式,如果你的程式碼不屬於這一類,祝你好運,你自己搞定吧,你這個寄生蟲)。如果你的驅動程式在主核心樹中,並且核心介面發生了變化,那麼最初進行核心更改的人會負責修復它。這確保了你的驅動程式始終可構建,並且隨著時間的推移正常工作,而你只需付出很少的努力。
將您的驅動程式納入主核心樹的非常好的副作用是:
驅動程式的質量將提高,因為維護成本(對原始開發者而言)將降低。
其他開發者會為您的驅動程式新增功能。
其他人會發現並修復您驅動程式中的錯誤。
其他人會在您的驅動程式中找到最佳化機會。
當外部介面需要更改時,其他人會為您更新驅動程式。
驅動程式會自動隨所有 Linux 發行版一起釋出,而無需請求發行版新增它。
由於 Linux 支援“開箱即用”的裝置數量比任何其他作業系統都多,並且它在比任何其他作業系統更多種處理器架構上支援這些裝置,這種經過驗證的開發模式一定做對了什麼 :)
感謝 Randy Dunlap、Andrew Morton、David Brownell、Hanna Linder、Robert Love 和 Nishanth Aravamudan 對本文早期草稿的審閱和評論。