2. 開發流程如何運作

1990年代早期,Linux 核心的開發過程相當鬆散,涉及的使用者和開發者相對較少。如今,核心的使用者基數已達數百萬,一年內約有2000名開發者參與其中,因此核心必須演變出許多流程以確保開發順利進行。要有效地參與其中,必須對開發流程有紮實的理解。

2.1. 概覽

核心開發者採用一種大致基於時間的釋出流程,每兩到三個月釋出一個新的主核心版本。最近的釋出歷史如下所示:

5.0

2019年3月3日

5.1

2019年5月5日

5.2

2019年7月7日

5.3

2019年9月15日

5.4

2019年11月24日

5.5

2020年1月6日

每個 5.x 版本都是一個主要核心版本,包含新功能、內部 API 變更等。一個典型的版本可能包含大約 13,000 個變更集,涉及數十萬行程式碼的修改。5.x 是 Linux 核心開發的前沿;核心採用持續整合重大變更的滾動開發模式。

對於每個版本的補丁合併,都遵循相對直接的紀律。在每個開發週期開始時,“合併視窗”被認為是開放的。屆時,被認為足夠穩定(並被開發社群接受)的程式碼將被合併到主線核心中。新開發週期的大部分變更(以及所有重大變更)將在此期間合併,速度接近每天 1,000 個變更(“補丁”或“變更集”)。

(順便一提,值得注意的是,在合併視窗期間整合的變更並非憑空出現;它們在此之前已經被收集、測試和暫存。該過程如何運作將在後續章節中詳細描述。)

合併視窗持續大約兩週。在此時間結束時,Linus Torvalds 將宣佈視窗關閉,併發布第一個“rc”核心。例如,對於註定成為 5.6 的核心,合併視窗結束時釋出的版本將稱為 5.6-rc1。-rc1 版本的釋出標誌著合併新功能的時間已過,穩定下一個核心的時間已開始。

在接下來的六到十週內,只有修復問題的補丁才應提交到主線。偶爾會允許進行更重大的更改,但這種情況很少見;試圖在合併視窗之外合併新功能的開發者往往會受到不友好的對待。一般來說,如果錯過了給定功能的合併視窗,最好的辦法是等待下一個開發週期。(對於以前不支援的硬體驅動程式,有時會有例外;如果它們不觸及樹內程式碼,它們就不會引起迴歸,並且可以隨時安全新增)。

隨著修復補丁進入主線,補丁速率會隨著時間減慢。Linus 大約每週釋出一個新的 -rc 核心;一個正常的系列會達到 -rc6 到 -rc9 之間,然後核心被認為足夠穩定併發布最終版本。屆時,整個過程將再次開始。

舉例來說,以下是 5.4 開發週期的進行方式(所有日期均為 2019 年):

9月15日

5.3 穩定版釋出

9月30日

5.4-rc1,合併視窗關閉

10月6日

5.4-rc2

10月13日

5.4-rc3

10月20日

5.4-rc4

10月27日

5.4-rc5

11月3日

5.4-rc6

11月10日

5.4-rc7

11月17日

5.4-rc8

11月24日

5.4 穩定版釋出

開發者如何決定何時關閉開發週期並建立穩定版本?最主要的衡量標準是之前版本的迴歸列表。雖然不歡迎任何 bug,但那些破壞過去正常執行系統的 bug 被認為特別嚴重。因此,導致迴歸的補丁不受歡迎,並且在穩定期很有可能被撤銷。

開發者的目標是在穩定版釋出之前修復所有已知的迴歸。但在現實世界中,這種完美很難實現;對於如此規模的專案,變數實在太多了。總有一個時候,延遲最終釋出只會讓問題變得更糟;等待下一個合併視窗的變更堆積會越來越大,下次會帶來更多的迴歸。因此,大多數 5.x 核心都會帶有一些已知迴歸釋出,儘管希望它們都不是嚴重的。

一旦釋出了穩定版,其持續維護將移交給“穩定團隊”,目前由 Greg Kroah-Hartman 負責。穩定團隊將使用 5.x.y 編號方案定期釋出穩定版的更新。要考慮更新發布,補丁必須 (1) 修復一個重要 bug,並且 (2) 已經合併到下一個開發核心的主線中。核心通常會在其初始釋出後,再接收略多於一個開發週期的穩定更新。例如,5.2 核心的歷史如下所示(所有日期均為 2019 年):

7月7日

5.2 穩定版釋出

7月14日

5.2.1

7月21日

5.2.2

7月26日

5.2.3

7月28日

5.2.4

7月31日

5.2.5

...

...

10月11日

5.2.21

5.2.21 是 5.2 版本的最終穩定更新。

有些核心被指定為“長期”核心;它們將獲得更長時間的支援。有關活躍的長期核心版本及其維護者列表,請參閱以下連結:

選擇一個核心進行長期支援,純粹是維護者有維護該版本的需要和時間。目前沒有關於任何特定即將釋出版本進行長期支援的已知計劃。

2.2. 補丁的生命週期

補丁並不會直接從開發者的鍵盤進入主線核心。相反,它會經歷一個有點複雜(但也有點非正式)的過程,旨在確保每個補丁都經過質量審查,並且每個補丁實現的更改都值得納入主線。對於小的修復,這個過程可能很快;而對於大型且有爭議的更改,則可能持續數年。許多開發者的挫敗感源於對這個過程缺乏理解,或者試圖規避它。

為了減少這種挫敗感,本文件將描述補丁如何進入核心。以下是對此過程的介紹,它以一種理想化的方式描述了該過程。更詳細的論述將在後續章節中出現。

補丁通常會經歷以下階段:

  • 設計。這是補丁的實際需求以及如何滿足這些需求的階段。設計工作通常在不涉及社群的情況下完成,但如果可能的話,最好在公開場合進行這項工作;這可以節省以後重新設計的大量時間。

  • 早期審查。補丁被髮布到相關的郵件列表,列表上的開發者會回覆他們可能有的任何評論。如果一切順利,這個過程應該會發現補丁的任何主要問題。

  • 更廣泛的審查。當補丁即將準備好納入主線時,它應該被相關的子系統維護者接受——儘管這種接受並不能保證補丁最終能進入主線。該補丁將出現在維護者的子系統樹和 -next 樹中(下文描述)。當流程正常執行時,此步驟將導致對補丁進行更廣泛的審查,並發現由於此補丁與他人正在進行的工作整合而導致的任何問題。

  • 請注意,大多數維護者也有日常工作,因此合併您的補丁可能不是他們的最高優先順序。如果您的補丁收到了需要更改的反饋,您應該進行這些更改或說明為什麼不應進行這些更改。如果您的補丁沒有審查投訴,但其對應的子系統或驅動維護者沒有合併它,您應該堅持將補丁更新到當前核心,使其能夠乾淨地應用,並繼續傳送以供審查和合並。

  • 合併到主線。最終,成功的補丁將被合併到由 Linus Torvalds 管理的主線倉庫中。此時可能會出現更多評論和/或問題;重要的是開發者要對這些問題做出響應並修復出現的任何問題。

  • 穩定版釋出。受補丁影響的使用者數量現在很大,因此,新問題可能再次出現。

  • 長期維護。雖然開發者在合併程式碼後確實有可能忘記程式碼,但這種行為往往會在開發社群留下不好的印象。合併程式碼減輕了一些維護負擔,因為其他人會修復由 API 變更引起的問題。但是,如果程式碼要長期保持有用,原始開發者應該繼續對程式碼負責。

核心開發者(或其僱主)犯下的最大錯誤之一是試圖將整個過程縮減為單一的“合併到主線”步驟。這種做法總是會讓所有相關人員感到沮喪。

2.3. 補丁如何進入核心

只有一個人可以將補丁合併到主線核心倉庫中:Linus Torvalds。但是,例如,在進入 2.6.38 核心的 9500 多個補丁中,只有 112 個(約 1.3%)是由 Linus 本人直接選擇的。核心專案早已發展到如此龐大的規模,以至於沒有一個開發者可以在沒有協助的情況下檢查和選擇每一個補丁。核心開發者解決這種增長的方式是使用基於信任鏈的下屬系統。

核心程式碼庫邏輯上分為一組子系統:網路、特定架構支援、記憶體管理、影片裝置等。大多數子系統都有一個指定的維護者,一個對該子系統中的程式碼負總責的開發者。這些子系統維護者是他們所管理核心部分的(鬆散意義上的)看門人;他們是(通常)接受補丁並將其納入主線核心的人。

每個子系統維護者管理自己的核心原始碼樹版本,通常(但並非總是)使用 git 原始碼管理工具。像 git(以及 quilt 或 mercurial 等相關工具)這樣的工具允許維護者跟蹤一個補丁列表,包括作者資訊和其他元資料。在任何給定時間,維護者都可以識別其倉庫中哪些補丁在主線中找不到。

當合並視窗開啟時,頂級維護者會要求 Linus “拉取”他們已選擇合併的補丁。如果 Linus 同意,補丁流將流入他的倉庫,成為主線核心的一部分。Linus 對拉取操作中收到的特定補丁的關注程度各不相同。很明顯,有時他會非常仔細地檢視。但作為一般規則,Linus 信任子系統維護者不會向上遊傳送糟糕的補丁。

子系統維護者反過來可以從其他維護者那裡拉取補丁。例如,網路樹是由首先在專門用於網路裝置驅動程式、無線網路等樹中累積的補丁構建的。這種倉庫鏈可以任意長,儘管它很少超過兩三個連結。由於鏈中的每個維護者都信任管理較低級別樹的維護者,因此這個過程被稱為“信任鏈”。

顯然,在這樣的系統中,將補丁引入核心取決於找到正確的維護者。直接將補丁傳送給 Linus 通常不是正確的方式。

2.4. -next 樹

子系統樹的鏈條引導著補丁流入核心,但也引出了一個有趣的問題:如果有人想檢視所有正在為下一個合併視窗準備的補丁怎麼辦?開發者會對其他待處理的更改感興趣,以檢視是否存在需要擔心的衝突;例如,一個更改核心核心函式原型的補丁將與任何使用該函式舊形式的其他補丁發生衝突。審閱者和測試人員希望在所有這些更改進入主線核心之前,以整合形式訪問這些更改。人們可以從所有有趣的子系統樹中拉取更改,但這將是一項龐大且容易出錯的工作。

答案以 -next 樹的形式出現,子系統樹在那裡被收集以進行測試和審查。這些樹中較舊的一個,由 Andrew Morton 維護,被稱為“-mm”(用於記憶體管理,這是它最初的由來)。-mm 樹集成了來自長長的子系統樹列表的補丁;它也有一些旨在幫助除錯的補丁。

除此之外,-mm 還包含 Andrew 直接選擇的大量補丁集合。這些補丁可能已經發布在郵件列表中,或者它們可能適用於核心的某個部分,而該部分沒有指定的子系統樹。因此,-mm 充當了最後一道防線的子系統樹;如果補丁沒有其他明顯的路徑進入主線,它很可能會進入 -mm。在 -mm 中累積的雜項補丁最終將要麼轉發給適當的子系統樹,要麼直接傳送給 Linus。在一個典型的開發週期中,大約 5-10% 進入主線的補丁是透過 -mm 進入的。

當前的 -mm 補丁可在“mmotm”(-mm of the moment)目錄中找到,地址是:

不過,使用 MMOTM 樹可能會令人沮喪;它甚至很可能無法編譯。

用於下一個週期補丁合併的主要樹是 linux-next,由 Stephen Rothwell 維護。linux-next 樹的設計目標是,它反映了下一個合併視窗關閉後主線預期會是什麼樣子。linux-next 樹在組裝時會在 linux-kernel 和 linux-next 郵件列表上公佈;可以從以下地址下載:

Linux-next 已成為核心開發過程中不可或缺的一部分;在一個給定合併視窗期間合併的所有補丁都應該在合併視窗開啟之前的一段時間內找到進入 linux-next 的途徑。

2.5. Staging 樹

核心原始碼樹包含 drivers/staging/ 目錄,其中包含許多正在新增到核心樹中的驅動程式或檔案系統的子目錄。它們在 drivers/staging 中保留,直到需要更多工作;一旦完成,它們就可以被移到核心的正確位置。這是一種跟蹤那些不符合 Linux 核心編碼或質量標準的驅動程式的方式,但人們可能希望使用它們並跟蹤開發進度。

Greg Kroah-Hartman 目前維護著 staging 樹。需要進一步改進的驅動程式會發送給他,每個驅動程式在 drivers/staging/ 中都有自己的子目錄。除了驅動程式原始檔之外,目錄中還應包含一個 TODO 檔案。TODO 檔案列出了驅動程式需要滿足才能被核心正式接受的待辦工作,以及任何針對該驅動程式的補丁應抄送的人員列表。當前的規則要求貢獻到 staging 的驅動程式至少必須能夠正確編譯。

Staging 可以是讓新驅動程式進入主線的一種相對容易的方式,運氣好的話,它們會引起其他開發者的注意並迅速改進。然而,進入 staging 並不是故事的結局;在 staging 中沒有定期進展的程式碼最終將被移除。發行版廠商也傾向於相對不情願啟用 staging 驅動程式。所以 staging 充其量只是通往成為一個正式主線驅動程式的中間站。

2.6. 工具

從以上文字可以看出,核心開發過程嚴重依賴於能夠將補丁集合導向不同方向的能力。如果沒有足夠強大的工具,整個過程將無法像現在這樣順利執行。關於如何使用這些工具的教程超出了本文件的範圍,但可以提供一些提示。

到目前為止,核心社群使用的主要原始碼管理系統是 Git。Git 是自由軟體社群中開發的一些分散式版本控制系統之一。它非常適合核心開發,因為它在處理大型倉庫和大量補丁時表現出色。它也有學習和使用困難的聲譽,儘管隨著時間的推移已經有所改善。對 Git 有一定程度的熟悉幾乎是核心開發者的必備條件;即使他們不將其用於自己的工作,他們也需要 Git 來跟上其他開發者(和主線)正在做的事情。

Git 現在幾乎由所有 Linux 發行版打包。它的主頁是:

該頁面包含指向文件和教程的指標。

在不使用 Git 的核心開發者中,最受歡迎的選擇幾乎肯定是 Mercurial:

Mercurial 與 Git 共享許多功能,但它提供了一個許多人認為更容易使用的介面。

另一個值得了解的工具是 Quilt:

Quilt 是一個補丁管理系統,而不是原始碼管理系統。它不跟蹤歷史;相反,它旨在跟蹤針對不斷演變的程式碼庫的特定變更集。一些主要的子系統維護者使用 Quilt 來管理計劃向上遊傳送的補丁。對於管理某些型別的樹(例如 -mm),Quilt 是最適合的工具。

2.7. 郵件列表

大量 Linux 核心開發工作是透過郵件列表完成的。如果不加入至少一個列表,就很難成為社群的正式成員。但 Linux 郵件列表也對開發者構成了潛在危險,他們可能被大量電子郵件淹沒,或者違反 Linux 列表中使用的慣例,或者兩者兼而有之。

大多數核心郵件列表託管在 kernel.org;主列表可以在以下地址找到:

還有一些列表託管在其他地方;請檢查 MAINTAINERS 檔案以獲取與任何特定子系統相關的列表。

當然,核心開發的核心郵件列表是 linux-kernel。這個列表是一個令人望而生畏的地方;郵件量每天可達 500 封,噪音很大,對話可能非常專業,參與者不總是注重禮貌。但沒有其他地方能讓核心開發社群作為一個整體聚集在一起;避免這個列表的開發者會錯過重要資訊。

以下是一些有助於在 linux-kernel 中生存的提示:

  • 將列表郵件投遞到單獨的資料夾,而不是您的主收件箱。必須能夠長時間忽略郵件流。

  • 不要試圖跟蹤每一次對話——沒有人能做到。重要的是要根據感興趣的主題(請注意,長時間執行的對話可能會偏離原始主題而電子郵件主題行不變)和參與者進行篩選。

  • 不要餵食噴子。如果有人試圖激起憤怒的回應,請忽略他們。

  • 回覆 linux-kernel 郵件(或其他列表上的郵件)時,請保留所有相關人員的 Cc: 標題。在沒有充分理由(例如明確要求)的情況下,您不應刪除收件人。請務必確保您正在回覆的人在 Cc: 列表中。此約定也使您無需明確要求在回覆您的帖子時抄送您。

  • 在提問之前搜尋列表檔案(和整個網路)。有些開發者可能會對那些顯然沒有做功課的人不耐煩。

  • 使用交錯式(“內聯”)回覆,這使您的回覆更易於閱讀。(即避免頂層回覆——將您的答案放在您回覆的引用文字之上)。更多詳情,請參閱Documentation/process/submitting-patches.rst

  • 在正確的郵件列表上提問。Linux-kernel 可能是總會面點,但它不是尋找所有子系統開發者的最佳地點。

最後一點——找到正確的郵件列表——是初級開發者常犯的錯誤。在 linux-kernel 上提問與網路相關的問題,幾乎肯定會收到禮貌的建議,轉而在 netdev 列表上提問,因為大多數網路開發者都在該列表上活躍。SCSI、video4linux、IDE、檔案系統等子系統都有其他列表。查詢郵件列表的最佳位置是核心原始碼包中的 MAINTAINERS 檔案。

2.8. 核心開發入門

關於如何開始核心開發流程的問題很常見——無論是來自個人還是公司。同樣常見的是,一些失誤會讓這種關係的開始比本來應該的更困難。

公司通常會尋求僱用知名開發者來啟動開發團隊。這確實是一種有效的技術。但它往往成本高昂,並且對增加經驗豐富的核心開發者池作用不大。只要投入一點時間,就可以讓公司內部的開發者快速掌握 Linux 核心開發。花時間進行這項工作可以讓僱主擁有一批既瞭解核心又瞭解公司的開發者,他們還可以幫助培訓其他人。從中長期來看,這通常是更具盈利性的方法。

個人開發者通常,可以理解地,不知道從何開始。從一個大型專案開始可能會令人望而生畏;人們通常希望先從小處著手試水。這是某些開發者開始建立修復拼寫錯誤或次要編碼風格問題的補丁的時候。不幸的是,此類補丁會產生一定程度的噪音,對整個開發社群造成干擾,因此,它們越來越受到鄙視。希望透過這些方式向社群介紹自己的新開發者將無法獲得他們所希望的歡迎。

Andrew Morton 對有抱負的核心開發者提出了以下建議:

The #1 project for all kernel beginners should surely be "make sure
that the kernel runs perfectly at all times on all machines which
you can lay your hands on".  Usually the way to do this is to work
with others on getting things fixed up (this can require
persistence!) but that's fine - it's a part of kernel development.

(https://lwn.net/Articles/283982/)。

在沒有明顯問題需要修復的情況下,建議開發者檢視當前的迴歸列表和一般的開放錯誤。需要修復的問題從不缺乏;透過解決這些問題,開發者將在過程中獲得經驗,同時,在開發社群中贏得尊重。