操作系統是如何啓動起來的?

大家好,我是小風哥。

操作系統被稱爲 “第一個程序”,the first programme,原因很簡單,只有當操作系統啓動起來後才能運行我們編寫的程序,那麼你有沒有想過這個問題:操作系統是怎樣啓動起來的呢?實際上這個過程就像發射火箭一樣有趣,看完這篇你就明白啦。

操作系統也是普通程序

哦對了,順便說下,小風哥建了一個微信技術羣,感興趣的同學可以加一下,拉到最後掃描二維碼備註 “加羣” 即可。

首先我們必須意識到這樣兩點:

  1. CPU 執行的是機器指令,編譯器將程序翻譯後成了機器指令

  2. 操作系統本身也是一個程序,這個程序被編譯後也會生成一大堆機器指令

現在我們知道了,操作系統本身其實也是一大堆機器指令,既然是機器指令那麼它必須得存放在什麼地方。

存放在哪裏呢?

想想我們編寫的程序,編譯後生成的是可執行文件,也就是說是以 “文件” 的形式存放的,並且存放在硬盤上,而操作系統也沒什麼不同,編譯後生成的機器指令同樣是以文件的形式存放的,存放在哪裏呢?可以存放在任何能存儲數據的介質,像 CD、磁盤之類都可以。

我們編寫的程序在啓動時被加載器——也就是 loader,加載到內存,加載器也是一個程序,這是一個加載其它程序的程序;這麼說可能有點拗口,但計算機系統中有大量類似的程序,編譯器是一個翻譯程序的程序、操作系統是一個運行其它程序的程序、鏈接器是一個鏈接程序的程序、解釋器是一個執行腳本程序的程序等等。

雞生蛋蛋生雞的問題

回到我們的主題,我們寫的代碼是 loader 加載到內存後運行的,那麼操作系統這個程序是也同樣的道理,必須得有個什麼東西也要把操作系統加載到內存中運行纔可以,這個東西不叫 loader,而是叫 boot loader,其本身也是一個程序,它的任務就是加載一個更大的程序,就像這裏的操作系統。

此時這裏會出現一個雞生蛋蛋生雞的,既然我們的程序是被加載器 loader(操作系統的一部分) 加載到內存中,而操作系統又是被 boot loader 這個加載程序加載到內存中的,那麼又是什麼加載器把 boot loader 這個加載程序加載到內存中呢?而又又是什麼加載器把上一句中的什麼加載器加載內存中呢?而又又又是什麼。。?

你會發現這個一個沒有出口的無窮遞歸啊有沒有,總得有個什麼把前一個程序加載到內存,就好比今天的前一天是昨天、昨天的前一天是前天、前天的前一天是大前天,如果一直這樣思考下去那麼時間到底在哪裏開始的呢?時間到底有沒有開始 (參考時間簡史或相對論)?

時間有沒有開始這個問題我不清楚,但操作系統啓動的這個問題我知道。

上述關於加載器以及加載加載器等問題全部指向了內存,讓我們好好想一想內存有什麼特殊性?

內存斷電後是無法保存數據

程序員都知道內存只有在加電的情況下才可以保存數據 (關於內存的實現原理你可以參考這篇《你管這破玩意叫 CPU?》),那麼很顯然,當斷電後內存中的內容就丟失了,那麼又很顯然的,當你在按下計算機開關通電時,內存中的內容是未被初始化的,也就是說內存中的內容是無效的,此時的內存裏還是一片荒蕪,這裏沒有任何東西可供 CPU 來執行,這就好比大爆炸之前的宇宙。

但我們的計算機總是能啓動起來,CPU 必須得執行 “一段什麼程序” 把第一個 boot loader 加載到內存中,由於此時內存中還什麼都沒有,那麼這段程序一定被保存在了其它地方

保存在了哪裏呢?

沒錯,這段程序就被保存在了 BIOS 的非易失性存儲 ROM 或者 flash 存儲中了,這裏的代碼在即使斷電後也會保存下來,加電後 CPU 開始執行這裏代碼,把 boot loader 加載到內存中,現在你應該明白第一個 boot loader 是怎樣被加載到內存的了吧。

在早期的計算機上甚至專門有一個按鈕,讓用戶自己選擇該從哪裏,比如打孔紙帶、打孔卡片或者硬盤,加載一個更復雜的程序來運行,操作面板上的旋鈕可以控制把這些程序加載到內存的什麼位置上去:

火箭與操作系統啓動

然而現實情況比較複雜,我們剛纔提到的 boot loader 這段小程序功能實在是太弱了,此時其能訪問的磁盤地址空間有限,不能把完整的內核全部加載到操作系統中,該怎麼辦呢?

既然 boot loader 比較弱那麼就換一個比較牛的 loader 程序來,就這樣出現了二階 boot loader,second stage loader:

二階 boot loader 功能更爲豐富,比如對硬件進行檢查、給用戶提供選項加載哪個操作系統等等,安裝多系統的同學應該知道,啓動時會給你一個選項到底是啓動 windows 還是 linux,這就是二階 boot loader 的作用。

最終,操作系統被二階 boot loader 加載到內存中開始運行。

你會發現這個過程就和發射三級火箭一樣,最初一級火箭啓動,燃料用盡後二級火箭啓動,二級火箭完成使命後三級火箭啓動,最終把衛星送到太空,而計算機的啓動過程也類似。

最初是 CPU 運行 BIOS 中的一段代碼把一級 boot loader 加載到內存中運行,該程序又會把二級 boot loader 加載到內存運行,而二級 boot loader 又會把操作系統加載到內存中,此後控制權被轉移到操作系統,(所謂控制權是指 CPU 跳轉到操作系統的代碼),操作系統開始運行,經過一系列的初始化,比如硬件檢測、開啓必要的後臺進程等等,最終圖形界面或者命令行界面呈現出來。

接下來我們把這個過程細化一下。

更詳細的啓動過程

你在按下電源的瞬間相當於火箭點火,此時一級發動機開始工作。

加電 CPU 重置後開始在地址 0xffff0 處開始執行指令,這個地址其實是 BIOS ROM 的末尾處,該位置其實是一個跳轉指令,跳轉到 ROM 的一段啓動代碼上,該代碼會進行必要的自檢,Power-on self-test (POST),展示 BIOS 啓動界面等等,最重要的一步是找到啓動設備,所謂啓動設備就是指從哪裏加載操作系統,比如 CD-ROM、或者磁盤、甚至 U 盤等都可以作爲啓動設備,早些年流行用 U 盤重新安裝系統,其實就是告訴 BIOS 的這段代碼從 U 盤中加載操作系統。

通常 BIOS 會把磁盤當做啓動設備 (大部分情況下),此時 BIOS 中的這段代碼開始將磁盤的第 0 號塊加載到內存中,那麼這第 0 號塊中有什麼呢?沒錯,就是第一階段 boot loader 程序,這第 0 號塊也被稱之爲 Master Boot Record,MBR,肯定有不少同學聽說過。

到這裏,火箭的一級發動機燃料用盡,二級發動機開始點火,BIOS 中的這段代碼把控制權交給加載到內存 boot loader,所謂控制權就是跳轉到 boot loader 程序,這樣 CPU 終於開始直接與內存交互了,CPU 開始從內存中取出指令然後執行。

MBR 中除了包含一段可執行代碼之外還有一個分區表,partition table,這個表的中的每一個條目本質上在說:“操作系統是否在我這個分區,我這個分區有多大”,CPU 在執行 MBR 中的代碼時會去檢查操作系統存在哪個分區中,定位後開始從相應分區的起始位置讀取磁盤數據到內存中,這時的磁盤數據中保存的就是二階 boot loader,second-stage boot loader,此時一階 boot loader 把控制權轉交給二階 boot loader,火箭三級發動機開始工作。

2_boot loader 的主要工作將操作系統加載到內存中,此後控制權轉交給操作系統,火箭的三級發動機完成使命,到這一時刻,操作系統開始接管計算機,操作系統經過一系列自身的初始化後創建出若干必要進程,至此計算機啓動完畢,衛星被成功送到了外太空中。

然而限於篇幅這裏依然沒有過多涉及細節,操作系統本身的初始化也是一個比較複雜的過程,感興趣的同學可以去翻閱相關操作系統的資料。

總結與腦洞

計算機的啓動是一個多階段的過程,當然在一些嵌入式設備等這個過程會簡化,但總體上也需要經過類似過程,只不過階段數會少一些。

回到最開始的那個問題,也就是時間有沒有開始,其實這個問題一些物理大牛已經回答過了,但我很想在這裏開一個腦洞,當上帝在爲自己創建的宇宙 (計算機) 加電的那一刻——也就是宇宙大爆炸時,時間開始了,時間這個概念是和宇宙 (計算機) 相伴相生的,如果沒有宇宙(計算機),時間這個概念其實是沒有意義的,就好比如果沒有計算機,加載這個概念其實是沒有意義的,你思考時間到底有沒有起點這個問題就好比計算機中的程序在思考到底是誰把自己加載到內存的、又是誰把操作系統加載到內存中的等等。。好啦,腦洞就開到這裏。

現在你應該明白計算機啓動這個問題了吧,啊啊啊, 可累死我了,覺得有幫助還不趕緊分享、點贊、收藏一鍵三連,要不你的良心過意的去嗎鐵子們**。**

這個公衆號裏所有的文章都已經彙總在了 Github 上,地址 https://github.com/xfenglu/everycodershouldknow ,求 star,哈哈。

最後推薦一下小風哥自己寫的書《深入理解操作系統》,如果你對操作系統感興趣同時也喜歡小風哥通俗易懂的寫作風格,這本書絕不能錯過。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/QDjAIzas5EXQ24-NfTU3TQ