1. 程式人生 > >【自制作業系統11】中場休息之細節是魔鬼

【自制作業系統11】中場休息之細節是魔鬼

如果你有幸看到這一章,那麼恭喜你,你已經完成了整個作業系統的一多半了,而且如果你前面的東西都完全掌握了,那後面無非就是順水推舟的事情了。本章不做繼續的講解,而是將之前的知識進行回顧,並把相似的知識點做對比。同時我也將到目前為止最大的感悟 細節是魔鬼 分享給大家。

一、到目前為止的程式流程圖

為了讓大家清楚目前的程式進度,畫了到目前為止的程式流程圖,如下。其實就是截至到記憶體管理這一塊

 二、回顧一下我們都做了些什麼

 這裡列出整個系列到目前為止的目錄,剛好也可以作為索引方便大家閱讀了

  • 【自制作業系統01】硬核講解計算機的啟動過程
  • 【自制作業系統02】環境準備與啟動區實現
  • 【自制作業系統03】讀取硬碟中的資料
  • 【自制作業系統04】從真實模式到保護模式
  • 【自制作業系統05】開啟記憶體分頁機制
  • 【自制作業系統06】終於開始用 C 語言了,第一行核心程式碼!
  • 【自制作業系統07】深入淺出特權級
  • 【自制作業系統08】中斷
  • 【自制作業系統09】中斷的程式碼實現
  • 【自制作業系統10】記憶體管理系統

  第 01 和 02 部分沒有實質性的程式碼,但卻是學習一個新東西的必要開始,也就是解除困惑,梳理流程。它告訴了我們 計算機的啟動過程,也就是計算機按下開機鍵之後,初始的程式計數器 IP 的值指向了哪裡,初始執行的 BIOS 中寫死的程式做了什麼,又跳轉到了哪裡。這讓我們至少擁有了能夠寫出一個讓電腦按下開機鍵之後不報錯併成功跳轉到我們所寫的程式碼的程式。之後的程式碼,就是在這兩部分所創造的基本框架中添油加醋完成的。

  第 03 部分是 讀取硬碟中的資料。我們所寫的程式肯定是放在硬盤裡的,需要載入到記憶體中才能被 CPU 一條一條取指令並執行。雖然 BIOS 幫我們完成了啟動區的載入任務,但這只是很小的一部分,我們不能及期望於將作業系統所有程式碼都寫到啟動區這 512 位元組中,所以啟動區裡的程式碼需要實現把更多在硬碟中的程式載入到記憶體中,這樣一個關鍵任務。

  第 04 部分是 真實模式到保護模式,這也是第一次 CPU 廠商和作業系統 打配合。我們只需要按照 CPU 要求的,比如 寫好段描述符結構、載入 gdt、將 cr0 的 pe 位置 1 等,就可以順利開啟 CPU 的保護模式,利用其保護模式的特徵了。

  第 05 部分是 開啟記憶體分頁機制,這又是一次 CPU 廠商和作業系統打配合的案例,也是按照 CPU 要求的步驟,把該有的資料結構(頁目錄表和頁表)寫好,就可以開啟分頁機制,利用其提供的分頁特徵了,由於打配合的案例越來越多,一會我單獨總結。

  第 06 部分是我們 第一次使用 C 語言 來做事,也可以看出,之前用匯編語言把那些 雜事 都處理乾淨了,真正寫核心主程式碼時,再把 C 語言請過來。我們學高階語言的人,包括我自己,都是認為越底層的語言越高階。不過從我們這個系列的角度看,彙編做的都是雜事,或者 C 語言不屑於做的事情,而 C 語言做的,才是“高大上”的事。所以學習這個系列,包括我自己寫這個系列,也是讓我從兩個角度來審視這個問題了,也再也不會去爭論到底高階語言高階,還是低階語言高階的事情了。

  第 07 部分是 深入淺出特權級,這部分也是沒有程式碼。這部分就提到了我們常說的 核心態、使用者態,只不過是從細節入手,講述了特權級是怎麼進行 保護、以及怎麼進行 跳轉 的。至於保護,自然有一套公式規則來判斷,至於跳轉,也是有一套跳轉規則,也就是什麼特權級,在什麼情況下,能跳到什麼特權級下。當然這只是簡單的描述,實際情況要複雜得多。

  第 08 和 09 部分是 中斷,從硬體講到了軟體,同時也指出了一個重要的思想,就是 作業系統是個死迴圈,是靠中斷驅動 的,我們的作業系統就是隨時等待著各種中斷進來,然後執行相應的中斷處理程式,為整個作業系統的執行方式做了個思想準備。

  第 10 部分是 記憶體管理系統,我們實現了簡單的四個 記憶體池 的管理,並最終實現了一個 申請核心記憶體空間 的函式,該函式自動在核心的虛擬記憶體池中和實體記憶體池中找到可用的記憶體頁,並將其對映到頁表中。這讓我們對記憶體管理有了個初步的認識。這塊就不再需要更多的 CPU 配合作業系統的部分了(當然基礎的都是需要的,我是說不再需要額外的),有點 純軟體設計 的感覺了,之前的部分或多或少都有跟硬體打交道的部分。我們慢慢地,將與硬體打交道的部分完善好,最後便只剩下軟體的天下了,也是我們軟體工程師越來越熟悉的部分。

  這也慢慢地,與我們的已有的知識接軌,什麼時候全串起來了,你就是高手了。技術領域的高手,不是看的高望得遠,而是基礎紮實,但要紮實到隨便把自己已有的知識進行關聯,就能得出一個很深刻的結論。

三、CPU 與作業系統打配合

我們不只一次提到了 CPU 與作業系統打配合的地方,現在我們就一起來串一串,把之前這種配合寫在一起對比一下。 

配合項 操作步驟1 操作步驟2 操作步驟3
開啟保護模式  開啟 A20 載入 gdt 將 cr0 的 pe 位置 1
載入 gdt 記憶體某位置初始化全域性描述符表 gdt lgdt 指令將 GDT 起始地址存入 gdtr 暫存器 將 cr0 的 pe 位置 1
開啟分頁 記憶體某位置初始化頁表 pde、pte 將頁目錄起始地址存入 cr3 暫存器 將 cr0 的 pg 位置 1
開啟中斷 記憶體某位置初始化中斷描述符表 idt,並初始化 pic lidt 指令將 idt 起始地址存入 idtr 暫存器  
特權級      

請自己默默體會其中的相似之處。

步驟相似,必然也存在著結構相似,下面我把到目前為止用到的結構列出

  至於後面的記憶體管理池的結構,那是我們軟體自己定義的,而上面列出的這些,是硬體定義的,也就是說作為程式設計師的我們,必須接受。

四、談談自己的理解與感悟

  這裡我沒有說談談自己對作業系統的理解,因為我覺得這裡談的可能超出了作業系統範疇。另外,我所談的真的是 到目前為止 的理解與感悟,並不是我已經把後面的知識學完了再來補的,可能都學完了再回憶自己在這個時候的感悟,已經不準確了。

  高階語言與低階語言沒有高下之分。我們普遍有個印象,就是外行人認為高階語言更高階,低階語言更低階,而內行人則相反。但我現在到覺得,它們根本沒有高下之分,只是不同角度罷了。如果從功能上說,低階語言能完成高階語言做不了的事情,也可以解釋高階語言無法解釋的原理,從這點看,低階語言顯得更高階。但如果從實際做的事情來看呢?本作業系統中,低階語言(也就是彙編)只是做了一些進入核心之前的雜事(當然一部分原因也是因為 c 語言做不了),一旦進入核心,只要能用 c 語言完成的,就絕對不用匯編了。從這個角度看,高階語言做的事情又比低階語言更高階一些。所以我的感悟就是,再也不去爭論誰高誰低了,而只是說什麼事情更適合什麼語言來做而已。我覺得就單單是這個感悟,對我目前的 Java 語言幫助就很大,我不再覺得 Java 比 C 語言或是組合語言更 low 了。

  細節是魔鬼。這真的是我最大的感悟,沒有之一。我起初學習作業系統的時候,總是想盡快,想著有些東西只要理解就可以了,沒必要去扣太多的細節。其實這是錯誤的,正確的做法是,先把細節扣得死死的,之後回過頭看的時候就再也不看細節了,開始注重總結與理解。而不是先就不扣細節,想著總結與理解。第一點是因為,細節掌握得足夠了,你會發現其他的好多知識點是通用的,根本不用看就可以理解了,比如 載入 gdt、載入 idt、以及載入分頁,如果你不是在看載入 gdt 這個部分時把細節吃透,後面兩個知識點你每個都要重新理解一遍,而且理解得還不一定透徹,所花的總時間,還不如你去花大量時間把第一個吃透,而後面兩個非常快速地就可以理解到很深入的地步。

  越簡單越透徹,越透徹越簡單。這裡的每一個知識點,我一開始都覺得比天都難,而在真正理解之後,卻變得很簡單,甚至有點無聊。你可能覺得這是廢話,但有的時候,有些知識點我理解的糊里糊塗,理解得也很複雜,跟別人也說不明白,當時我覺得是知識點就很複雜,給被人講肯定是講不明白的,得人家自己去理解。但後來我發現,無論多複雜的事,只要你真正掌握並且抓到本質了,是真的很簡單,甚至你可以給一個不搞技術的人講明白(這個明白當然不是真正的懂哈)。就比如說記憶體管理這一塊,我看的時候甚至一度要放棄,但後來經過對每一行程式碼,每一個結構都扣出來剖析之後,我竟然覺得這塊簡單得不要不要的,無非就是取一個虛擬地址,取一個實體地址,再把它們加到頁表中對應起來。不過這塊也同時說明了 細節是魔鬼。倘若你一開始就看到這句話,比如從某個一分鐘搞懂作業系統的軟文,你的理解就是“取一個虛擬地址,取一個實體地址,再把它們加到頁表中對應起來”,和我扣完細節之後的理解一樣,但你覺得這是真正的一樣麼?所以,不是因為它簡單所以你理解的透徹,而是因為你理解的透徹,它就變得簡單起來了。

   所以,細節真的是魔鬼,千萬不要企圖一篇文章就搞懂作業系統這種事,我也可以把這篇文章改編成一個類似的文章,但如果你不去花時間扣細節,你今後一定會把更多的時間耽誤在這裡的,這個賬總會有一天找到你的頭上。

寫在最後:開源專案和課程規劃

如果你對自制一個作業系統感興趣,不妨跟隨這個系列課程看下去,甚至加入我們,一起來開發。

參考書籍

《作業系統真相還原》這本書真的贊!強烈推薦

專案開源

專案開源地址:https://gitee.com/sunym1993/flashos

當你看到該文章時,程式碼可能已經比文章中的又多寫了一些部分了。你可以通過提交記錄歷史來檢視歷史的程式碼,我會慢慢梳理提交歷史以及專案說明文件,爭取給每一課都準備一個可執行的程式碼。當然文章中的程式碼也是全的,採用複製貼上的方式也是完全可以的。

如果你有興趣加入這個自制作業系統的大軍,也可以在留言區留下您的聯絡方式,或者在 gitee 私信我您的聯絡方式。

課程規劃

本課程打算出系列課程,我寫到哪覺得可以寫成一篇文章了就寫出來分享給大家,最終會完成一個功能全面的作業系統,我覺得這是最好的學習作業系統的方式了。所以中間遇到的各種坎也會寫進去,如果你能持續跟進,跟著我一塊寫,必然會有很好的收貨。即使沒有,交個朋友也是好的哈哈。

目前的系列包括

  • 【自制作業系統01】硬核講解計算機的啟動過程
  • 【自制作業系統02】環境準備與啟動區實現
  • 【自制作業系統03】讀取硬碟中的資料
  • 【自制作業系統04】從真實模式到保護模式
  • 【自制作業系統05】開啟記憶體分頁機制
  • 【自制作業系統06】終於開始用 C 語言了,第一行核心程式碼!
  • 【自制作業系統07】深入淺出特權級
  • 【自制作業系統08】中斷
  • 【自制作業系統09】中斷的程式碼實現
  • 【自制作業系統10】記憶體管理系統

 微信公眾號

  我要去阿里(woyaoqual