Hello OS

操做系統對於每一個開發者來講都是繞不開的門檻,不論是傳統的單片機也好,仍是如今分佈式系統也好,都是離不開基本是計算機模型,從圖靈機到馮諾依曼,從埃尼阿克到如今太湖之光,這幾十年來的計算機發展都仍是在這個模型下發展起來的,能夠說在量子計算機大規模推廣以前,現今的操做系統軟件仍是很值得學習借鑑。俗話說,它山之石能夠攻玉,那麼咱們本身磨石頭,或許也能夠發現蘊含在石頭中的璞玉,這也是一件很值得期待的事情呢,不是嗎?docker

最近就想本身動手實驗本身寫個操做系統,本文權且做爲本系列做品的開篇之做,按照我對操做系統的認知來層層推動,最終的指望固然是本身寫出個性化的操做系統啦,有機會的話,再繼續深刻到分佈式操做系統,進而進入雲操做系統,想一想也是挺刺激的,試試看唄,看看能作到多少~網絡上資料這麼多,牛人這麼強,應該能夠啦編程

OS以前

Hello OS以前,先要搞清楚所謂的操做系統在上電以後的引導流程,總結來講以下圖所示:網絡

Hello OS

簡單來講PC機的BIOS固件是一種已經固化在PC機主板上的 ROM芯片中的操做系統,即便掉電也能保存,而PC機上電後的第一條指令就是在BIOS固件中的,它負責檢測和初始化 CPU、內存及主板平臺,而後加載引導設備(大機率是硬盤)中的第一個扇區數據,到0x7c00地址開始的內存空間,再接着跳轉到0x7c00 處執行指令,其實就是執行GRUB引導程序。分佈式

此次實驗是用來體驗一下本身編譯一個.bin文件,而後修改的Ubuntu引導程序,進而啓動編譯完成的這個系統文件。ide

Hello OS引導的彙編代碼

知道PC機的上電流程以後,就能夠開始進行逐步開發了,好比說利用一下彙編語言來進行引導程序的開發entry.asm函數

MBT_HDR_FLAGS EQU 0x00010003
MBT_HDR_MAGIC EQU 0x1BADB002 ;多引導協議頭魔數
MBT_HDR2_MAGIC EQU 0xe85250d6 ;第二版多引導協議頭魔數
global _start ;導出_start符號
extern main ;導入外部的main函數符號
[section .start.text] ;定義.start.text代碼節
[bits 32] ;彙編成32位代碼
_start:
jmp _entry
ALIGN 8
mbt_hdr:
dd MBT_HDR_MAGIC
dd MBT_HDR_FLAGS
dd -(MBT_HDR_MAGIC+MBT_HDR_FLAGS)
dd mbt_hdr
dd _start
dd 0
dd 0
dd _entry
;以上是GRUB所須要的頭
ALIGN 8
mbt2_hdr:
DD MBT_HDR2_MAGIC
DD 0
DD mbt2_hdr_end - mbt2_hdr
DD -(MBT_HDR2_MAGIC + 0 + (mbt2_hdr_end - mbt2_hdr))
DW 2, 0
DD 24
DD mbt2_hdr
DD _start
DD 0
DD 0
DW 3, 0
DD 12
DD _entry
DD 0
DW 0, 0
DD 8
mbt2_hdr_end:
;以上是GRUB2所須要的頭
;包含兩個頭是爲了同時兼容GRUB、GRUB2
ALIGN 8
_entry:
;關中斷
cli
;關不可屏蔽中斷
in al, 0x70
or al, 0x80
out 0x70,al
;從新加載GDT
lgdt [GDT_PTR]
jmp dword 0x8 :_32bits_mode
_32bits_mode:
;下面初始化C語言可能會用到的寄存器
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
xor edi,edi
xor esi,esi
xor ebp,ebp
xor esp,esp
;初始化棧,C語言須要棧才能工做
mov esp,0x9000
;調用C語言函數main
call main
;讓CPU中止執行指令
halt_step:
halt
jmp halt_step
GDT_START:
knull_dsc: dq 0
kcode_dsc: dq 0x00cf9e000000ffff
kdata_dsc: dq 0x00cf92000000ffff
k16cd_dsc: dq 0x00009e000000ffff
k16da_dsc: dq 0x000092000000ffff
GDT_END:
GDT_PTR:
GDTLEN dw GDT_END-GDT_START-1
GDTBASE dd GDT_START

以上的彙編代碼分爲 4 個部分:工具

  1. 代碼 1~40 行,用匯編定義的GRUB的多引導協議頭,其實就是必定格式的數據,Hello OS 是用 GRUB 引導的,固然要遵循GRUB多引導協議標準,讓 GRUB 能識別的 Hello OS。而之因此有兩個引導頭,是爲了兼容 GRUB1 和 GRUB2。
  2. 代碼 44~52 行,關掉中斷,設定 CPU 的工做模式。
  3. 代碼 54~73 行,初始化 CPU 的寄存器和 C 語言的運行環境。
  4. 代碼 78~87 行,GDT_START開始的,是 CPU 工做模式所須要的數據

主函數

上面的彙編代碼調用了main函數,而在其代碼中並無看到其函數體,而是從外部引入了一個符號。那是由於這個函數是用 C 語言寫的在main.c中,最終它們分別由nasmGCC 編譯成可連接模塊,由LD 連接器連接在一塊兒,造成可執行的程序文件:學習

#include "vgastr.h"
void main(){ 
    printf("Hello OS!"); 
}

這裏用到的printf也不是咱們熟知的那個函數,只是碰巧名字同樣罷了,這個顯示函數是須要咱們本身實現的。調皮一些的話,printf還能夠改爲echoshowkankanwo這些,不要緊的,畢竟這個函數也是咱們本身定義的。測試

控制計算機屏幕

咱們之因此能夠看到的屏幕顯示的內容,是由於有個硬件的來支撐的,即咱們常說的顯卡。若是咱們要在屏幕上顯示字符,本質上就是編程操做顯卡。這個並不難,作完了甚至還挺有成就感。操作系統

注意到不管咱們 PC 上是什麼顯卡,它們都支持一種叫VESA的標準,這種標準下有兩種工做模式:字符模式和圖形模式。顯卡們爲了兼容這種標準,不得不本身提供一種叫VGABIOS 的固件程序。

這裏須要補充一下在上古時代顯卡的字符模式的工做細節。它把屏幕分紅 24 行,每行 80 個字符,把這(24*80)個位置映射到以0xb8000地址開始的內存中,每兩個字節對應一個字符,其中一個字節是字符的ASCII碼,另外一個字節爲字符的顏色值。以下圖所示:

Hello OS

瞭解細節以後就能夠對顯示程序vgastr.c進行開發

void _strwrite(char* string)
{
    char* p_strdst = (char*)(0xb8000);
    while (*string)
    {

        *p_strdst = *string++;
        p_strdst += 2;
    }
    return;
}

void printf(char* fmt, ...)
{
    _strwrite(fmt);
    return;
}

代碼很簡單,printf函數直接調用了_strwrite 函數,而_strwrite函數正是將字符串裏每一個字符依次找到以0xb8000 地址開始的顯存中,而p_strdst每次加2,則是爲了跳過字符的顏色信息的空間。

編譯和安裝

Hello OS 的代碼都已經寫好,這時就要進入安裝測試環節了。不過在安裝以前,還要進行系統編譯,即把每一個代碼模塊編譯最後連接成可執行的二進制文件。

make

make 歷史悠久,小巧方便,也是不少成熟操做系統編譯所使用的構建工具。

咱們在軟件開發中,make是一個工具程序,它讀取一個叫makefile的文件,也是一種文本文件,這個文件中寫好了構建軟件的規則,它能根據這些規則自動化構建軟件,就相似咱們用docker打包的時候,須要寫dockerfile同樣。

任何一個 Linux 發行版中都默認自帶這個 make 程序,因此不須要額外的安裝工做,咱們直接使用便可。

編譯

下面咱們用一張圖來描述咱們 Hello OS 的編譯過程,以下所示

Hello OS

安裝 Hello OS

通過上述流程,能夠獲得Hello OS.bin文件,可是還要讓GRUB可以找到它,才能在計算機啓動時加載它。這個過程稱爲安裝,不過這裏沒有寫安裝程序,得咱們手動來作。經研究發現,GRUB 在啓動時會加載一個grub.cfg 的文本文件,根據其中的內容執行相應的操做,其中一部份內容就是啓動項。GRUB 首先會顯示啓動項到屏幕,而後讓咱們選擇啓動項,最後 GRUB 根據啓動項對應的信息,加載 OS 文件到內存。

menuentry 'HelloOS' { 
        insmod part_msdos #GRUB加載分區模塊識別分區 
        insmod ext2 #GRUB加載ext文件系統模塊識別ext文件系統 
        set root='hd0,msdos1' #注意boot目錄掛載的分區,這是我機器上的狀況 
        multiboot2 /boot/HelloOS.bin #GRUB以multiboot2協議加載HelloOS.bin 
        boot #GRUB啓動HelloOS.bin
}

關於root的配置狀況,若是不知道本身boot分區的掛載點,能夠在grub的引導程序上面按C

Hello OS

進入GRUB的命令行,而後查看提供的掛載分區,這回沒有巧辦法了,只能一步步調試了,改掛載的分區點,最重要的是,記得把make以後生成的HelloOS.bin文件拷貝到\boot目錄下

Hello OS

以後就正常啓動吧:)完成本次實驗

Hello OS

總結一下

此次實驗先從按下 PC 機電源開關開始,窺探了PC 機的引導過程。它從 CPU 上電,到加載 BIOS 固件,再由 BIOS 固件對計算機進行自檢和默認的初始化,並加載 GRUB 引導程序,最後由 GRUB 加載具體的操做系統。其次,用匯編語言和 C 語言實現 Hello OS。第一步,用匯編程序初始化 CPU 的寄存器、設置 CPU 的工做模式和棧,最重要的是加入了 GRUB 引導協議頭;第二步,切換到 C 語言,用 C 語言寫好了主函數和控制顯卡輸出的函數,這個時候還須要瞭解顯卡的一些工做細節。最後,就是編譯和安裝 Hello OS 了。我用了 make 工具編譯整個代碼,其實 make 會根據一些規則調用具體的 nasm、gcc、ld 等編譯器,而後造成 Hello OS.bin 文件,最後把這個文件寫複製到 boot 分區,寫好 GRUB 啓動項,這樣就行了。

嗯,期待下次更深刻的探索O(∩_∩)O

相關文章
相關標籤/搜索