進行 Linux 核心與韌體開發的時候,往往需要多次的重新啟動,會浪費大把的時間。
在所有我擁有或使用過的電腦中,啟動最快的那台是 20 世紀 80 年代的電腦。在你把手從電源鍵移到鍵盤上的時候,BASIC 直譯器已經在等待你輸入命令了。對於現代的電腦,啟動時間從筆記型電腦的 15 秒到小型家庭伺服器的數分鐘不等。為什麼它們的啟動時間有差別?
那台直接啟動到 BASIC 命令列提示符的 20 世紀 80 年代微電腦,有著一顆非常簡單的 CPU,它在通電的時候就立即開始從一個記憶體地址中獲取和執行指令。因為這些系統的 BASIC 在 ROM 裡面,基本不需要載入的時間——你很快就進到 BASIC 命令提示字元中了。同時代更加複雜的系統,比如 IBM PC 或 Macintosh,需要一段可觀的時間來啟動(大約 30 秒),儘管這主要是因為需要從軟碟上讀取作業系統的緣故。在可以載入作業系統之前,只有很小一部分時間是花費在韌體上的。
現代伺服器往往在從磁碟上讀取作業系統之前,在韌體上花費了數分鐘而不是數秒。這主要是因為現代系統日益增加的複雜性。CPU 不再能夠只是執行起來就開始全速執行指令,我們已經習慣於 CPU 頻率變化、節省能源的待機狀態以及 CPU 多核。實際上,在現代 CPU 內部有數量驚人的更簡單的處理器,它們協助主 CPU 核心啟動並提供執行時服務,比如在過熱的時候壓制頻率。在絕大多數 CPU 架構中,在你的 CPU 內的這些核心上執行的程式碼都以不透明的二進位制 blob 形式提供。
在 OpenPOWER 系統上,所有執行在 CPU 內部每個核心的指令都是開源的。在有 OpenBMC(比如 IBM 的 AC922 系統和 Raptor 的 TALOS II 以及 Blackbird 系統)的機器上,這還延伸到了執行在基板管理控制器上的程式碼。這就意味著我們可以一探究竟,到底為什麼從接入電源線到顯示出熟悉的登入介面花了這麼長時間。
如果你是核心相關團隊的一員,你可能啟動過許多核心。如果你是韌體相關團隊的一員,你可能要啟動許多不同的韌體映像,接著是一個作業系統,來確保你的韌體仍能工作。如果我們可以減少硬體的啟動時間,這些團隊可以更有生產力,並且終端使用者在搭建系統或重新啟動安裝韌體或系統更新的時候會對此表示感激。
過去的幾年,Linux 發行版的啟動時間已經做了很多改善。現代的初始化系統在處理並行和按需任務上做得很好。在一個現代系統上,一旦核心開始執行,它可以在短短數秒內進入登入提示符介面。這裡短短的數秒不是優化啟動時間的下手之處,我們要到更早的地方:在我們到達作業系統之前。
在 OpenPOWER 系統上,韌體通過啟動一個儲存在韌體快閃記憶體晶片上的 Linux 核心來載入作業系統,它執行一個叫做 Petitboot 的使用者態程式去尋找使用者想要啟動的系統所在磁碟,並通過 kexec 啟動它。有了這些優化,啟動 Petitboot 環境只占了啟動時間的百分之幾,所以我們還得從其他地方尋找優化項。
在 Petitboot 環境啟動前,有一個先導韌體,叫做 Skiboot,在它之前有個 Hostboot。在 Hostboot 之前是 Self-Boot Engine,一個晶圓切片(die)上的單獨核心,它啟動單個 CPU 核心並執行來自 Level 3 快取的指令。這些元件是我們可以在減少啟動時間上取得進展的主要部分,因為它們花費了啟動的絕大部分時間。或許這些元件中的一部分沒有進行足夠的優化或盡可能做到並行?
另一個研究路徑是重新啟動時間而不是啟動時間。在重新啟動的時候,我們真的需要對所有硬體重新初始化嗎?
正如任何現代系統那樣,改善啟動(或重新啟動)時間的方案已經變成了更多的並行執行、解決遺留問題、(可以認為)作弊的結合體。