JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

電腦如何開機,以及核心如何被建造

當你按下電源鍵時,記憶體裡還沒有作業系統,那是誰把它載入的呢?讓我們順著把核心拉進生命的那條鏈條走一遍,再窺探核心本身,看看那些塑造每一個作業系統的設計抉擇。

開機的雞生蛋、蛋生雞難題

在這個階梯裡我們一路建立起來的一切,都假設核心已經在執行、已經坐在核心模式裡、隨時準備好回應一次系統呼叫。但當你第一次按下電源鍵時,這些都還不存在。記憶體裡裝的是垃圾、硬碟只是一坨位元、沒有任何程式在掌權。那個負責載入程式的東西,本身就是一支必須被某種方式載入的程式。這就是啟動電腦的雞生蛋難題:誰來載入那個載入器?

答案是一場接力賽。每一棒選手都很小,只聰明到足以找到下一棒並交出控制權。比賽從一小段燒進主機板晶片裡的程式碼開始,那段程式碼在任何硬碟被讀取之前就已存在。那段韌體會找到一支稍大的程式,那支再找到更大的一支,最後才找到並載入核心。開機(boot)一詞是 bootstrap(鞋帶)的縮寫,源自那個荒謬的畫面:抓著自己的鞋帶把自己提起來,而這正是機器看起來在做的事。

開機接力,一棒一棒走

讓我們在一台典型的現代 PC 上追蹤開機流程。火炬從韌體傳到一支小型載入器、再傳到核心,每一次交棒都收窄了正在執行的東西、卻拓寬了它能做的事。在較舊的機器上韌體叫做 BIOS;今天則幾乎一律是 UEFI,一種更豐富的韌體,懂得現代硬碟、也能驗證簽章。

  1. 通電。CPU 從晶片裡寫死的一個固定位址開始執行韌體程式碼。韌體跑一次快速自我測試,接著探測記憶體、硬碟、鍵盤與其他硬體。
  2. 找到開機裝置。韌體讀取所選硬碟最前面的一小塊資料(早期稱為開機區塊),並從中找出開機載入器,這支小程式唯一的任務就是載入一個作業系統。
  3. 載入開機載入器。開機載入器可能會顯示一個選單(選 Linux 或 Windows),然後把核心映像從硬碟讀進記憶體。在 Linux 上它通常還會載入一個叫做 initramfs 的迷你暫時根檔案系統,好讓核心擁有抵達真正硬碟所需的驅動程式。
  4. 把控制權交給核心。開機載入器跳到核心的進入點。核心設定記憶體管理、把 CPU 牢牢切換進核心模式、啟動自己的驅動程式、掛載真正的檔案系統,最後啟動第一支使用者空間行程。
  5. 啟動第一支行程。那第一支行程(常叫做 init 或 systemd)成為其他所有程式的祖先。從這裡開始,你熟悉的系統就活了起來:登入畫面、各種服務、你的應用程式。

注意這個形狀:一支短小、笨拙、受信任的程式去載入一支較長、較聰明、較不受信任的程式,一遍又一遍。等到真正的應用程式執行時,這條鏈已經經過三、四個階段的交棒。許多韌體在這裡加上一個安全步驟——安全開機——每一階段在執行下一階段之前,會用密碼學檢查它的簽章,於是被竄改過的載入器或核心就會被拒絕。

塵埃落定後,記憶體裡坐著什麼

開機之後,記憶體被分成兩半。核心占據自己受保護的區域,並在核心模式下執行,能存取每一條特權指令;其餘的一切——你的 shell、你的瀏覽器、登入畫面——都在使用者模式下、各自的空間裡執行。CPU 的模式位元隨時記得是哪一邊在掌權,而它只透過一道受控的門切換到核心那一側,也就是一次系統呼叫所觸發的那個陷阱。這就是你在本階梯前面遇過的那條大分界;開機,不過是這條分界第一次被畫出來的方式。

開機也是上一篇導引裡那些想法成真的地方。核心一啟動就立刻開啟中斷與一個計時器,於是它能實行分時:把 CPU 交給某支程式,再在下一個時脈滴答時把它奪回來、讓另一支輪到。正是這種飛快的拋接,讓多重程式設計能同時把許多程式留在記憶體裡。這一切在核心起來之前都不會發生,而這正是開機之所以重要的全部原因。

核心本身怎麼建:三張藍圖

既然核心已經在執行,那它裡面有什麼?一個核心必須提供許多服務——行程排程、記憶體管理、檔案系統、網路堆疊、數十支裝置驅動程式。最大的設計問題,也就是核心結構的核心,是:這些東西當中哪些跑在受保護的核心裡、哪些當作普通的使用者模式程式來跑?你把這條內部界線畫在哪裡,就生出三張經典藍圖。

第一張是單體式核心(monolithic kernel)。一切——排程器、驅動程式、檔案系統、網路——全部住在一起,作為一支跑在核心模式裡的大程式。它就像一間每個科室共用一整片開放樓層的醫院:醫生能伸手橫過房間把病歷交給護士,所以快得不得了。代價是脆弱:一支有 bug 的驅動程式與其他一切共用同一塊位址空間,因此它能毀掉整個核心、把整台機器拖垮。Linux 核心就是著名的例子,不過它用可載入核心模組緩和了這種僵硬——模組可以在執行期插上拔下。

第二張是微核心(microkernel)。這裡核心被維持得極小:它幾乎只做最基本的事——基本排程、基本記憶體保護,以及訊息傳遞。檔案系統、驅動程式、網路堆疊都被推出去,成為獨立的使用者模式伺服器行程,它們透過行程間通訊彼此交談、也與客戶端交談。想像每個醫院科室都在自己上鎖的門後,透過一道投信口傳紙條。要是某個科室燒掉了,其他科室照常運作,而你還能把它重啟。代價是速度:每張穿過投信口的紙條都是一趟 IPC 來回,原本在單體式核心裡是一次函式呼叫的事,在這裡變成好幾則跨越核心邊界的訊息。

混合式,以及真實系統到底怎麼做

實務上幾乎沒有任何主流作業系統是純微核心,因為對最熱門的路徑來說,IPC 成本太陡了。所以大多數真實核心都是混合式(hybrid):它們保留微核心設計的結構與許多伺服器,卻把對效能極為關鍵的部分(例如排程器或圖形路徑)拉回核心模式裡以求速度。混合式核心是一種誠實的工程折衷,而不是逃避。Windows NT 架構與蘋果的 XNU 核心都被如此描述。

                kernel mode | user mode
  monolithic:  [ALL services]| apps
  microkernel: [tiny core]   | fs, drivers, net (servers) + apps
  hybrid:      [core + hot]  | some servers + apps
每張藍圖把核心模式與使用者模式之間的界線畫在哪裡。

還有一些更激進、值得記住名字的藍圖。外核心把核心剝得更薄,直接把原始硬體暴露給應用程式、讓它們實作自己的抽象。單核心映像(unikernel)則走向反方向,把單一應用程式與它所需的那幾塊作業系統部件融合成一個可開機的映像,完全沒有使用者/核心之分,適合範圍緊縮的雲端工作負載。這些不是跑在你筆電上的東西,但它們顯示出:核心與使用者之間的那條線是一種設計抉擇,而非自然法則。

退一步看,整個階梯就咬合在一起了。作業系統是資源管理者,也是抽象層;核心是那個有特權的核心地帶,負責執行它與你的程式之間的分界;系統呼叫是穿過這道分界的門;多重程式設計與分時是它如何運用 CPU;開機是這一切如何被開啟;而核心結構則是管理員自家辦公室的平面圖。你現在已經掌握了完整的一樓,而上面的階梯會把每一個房間布置起來。