讓我們一起學習,由簡單的Hello OS入手,最終碼出一個屬于自己的操作系統(tǒng)!
前言
為什么要學寫一個操作系統(tǒng)?
為什么要學寫一個操作系統(tǒng)?
為什么要學寫一個操作系統(tǒng)?
眾所周知,操作系統(tǒng)是所有軟件的基礎(也是面試的三板斧之一?。?/strong>
對于后端開發(fā)而言,操作系統(tǒng)層面的進程、線程、內(nèi)存、I/O等相關知識根本逃不掉;
對于WEB開發(fā),性能調(diào)優(yōu)更是離不開操作系統(tǒng);
對于運維、測試,如果操作系統(tǒng)學的好,那么在出現(xiàn)問題時定位速度和解決bug的速度一定會提升;
對于非專業(yè)人員來說,操作系統(tǒng)在日常生活中更是無處不在,手機、智能手表、電腦、路由器等等都是各種操作系統(tǒng),了解一些操作系統(tǒng)的知識可以讓你快樂(裝逼)。
總之,對于感興趣的同學而言,操作系統(tǒng)不妨看一看,學一學。
Hello OS
引導程序
剛開始我們不來那么硬核的知識,而是直接動手寫一個最微型的操作系統(tǒng)——Hello OS。等擼完了,我們再回過頭來看看它的細節(jié)!
一般來說機器加電啟動后,整個計算機第一個啟動的程序就是固化在PC主板上的BIOS固件,它啟動之后檢測系統(tǒng)參數(shù),如內(nèi)存的大小、日期和時間、磁盤設備以及這些磁盤設備用來引導的順序。BIOS尋找用于裝載操作系統(tǒng)的指令。裝載操作系統(tǒng)的這個程序就是boot loader。Linux系統(tǒng)默認的boot loader就是GRUB(GRand Unified Bootloader),于是PC上電以后系統(tǒng)啟動流程如下:
[圖片上傳失敗...(image-2afb4b-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
接下來我們要做的就是寫一個由GRUB引導的“操作系統(tǒng)”——它會在屏幕上顯示"Hello OS"。注:****其實操作系統(tǒng)歸根結底也是一個程序,只不過它在開機之后已經(jīng)運行,并且權限和功能高的嚇人,你可以將它看成是整個計算機應用的管家角色。
環(huán)境準備
下載VMware,并安裝Ubuntu16.04鏡像,安裝鏈接如下:
https://blog.csdn.net/stpeace/article/details/78598333
安裝成功之后,打開虛擬機。
[圖片上傳失敗...(image-df3671-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
下載源代碼
在虛擬機(Vmware)中打開terminal,使用git clone下載源代碼。如果提示git command沒找到,輸入命令:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">sudo apt-get install git </pre>
代碼結構如下:
[圖片上傳失敗...(image-5ba478-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
其中,
- entry.asm:是一段匯編代碼,用作GRUB引導調(diào)用,關掉中斷,設定CPU工作模式,初始化寄存器及C語言運行環(huán)境等;
- hello.lds:進行鏈接調(diào)用,代碼簡單看看,反正也看不懂:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">ENTRY(_start) OUTPUT_ARCH(i386) OUTPUT_FORMAT(elf32-i386) SECTIONS { . = 0x200000; __begin_start_text = .; .start.text : ALIGN(4) { *(.start.text) } __end_start_text = .; __begin_text = .; .text : ALIGN(4) { *(.text) } __end_text = .; __begin_data = .; .data : ALIGN(4) { *(.data) } __end_data = .; __begin_rodata = .; .rodata : ALIGN(4) { *(.rodata) *(.rodata.*) } __end_rodata = .; __begin_kstrtab = .; .kstrtab : ALIGN(4) { *(.kstrtab) } __end_kstrtab = .; __begin_bss = .; .bss : ALIGN(4) { *(.bss) } __end_bss = .; } </pre>
- install.md:該文件中是GRUB引導的配置內(nèi)容,需要將這個文件里的內(nèi)容復制到GRUB的cfg配置文件中,才能使電腦開機時可以找到我們的Hello OS;
- main.c:我們Hello OS的主函數(shù),它調(diào)用的printf可不是常見的C語言庫函數(shù)哦,而是我們自己實現(xiàn)的printf!即下面要講的vgastr.h:
[圖片上傳失敗...(image-6d0607-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
- vgastr.h:控制計算機屏幕VGABIOS固件程序顯示特定字符,后面詳細介紹;
- Makefile:利用make工具來實現(xiàn)編譯源代碼,主要是將entry.asm、main.c、vgastr.h編譯并鏈接。
編譯操作系統(tǒng)
流程
Makefile文件中其實已經(jīng)寫出了如何編譯我們的操作系統(tǒng),有興趣的可以打開看看,篇幅太長就不放了。(但這其實很重要,最好去看看,并學學make 或者cmake)
整個編譯流程就如圖:
[圖片上傳失敗...(image-74da22-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
編譯
在當前文件路徑下打開terminal,輸出指令:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">make all </pre>
emm.....果然不出所料沒有安裝nasm編譯器:
[圖片上傳失敗...(image-e0ed28-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
輸入命令:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">sudo apt-get install nasm </pre>
安裝完畢之后再次運行,報了一個warning,暫時不管:
[圖片上傳失敗...(image-a537e7-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
經(jīng)過編譯,我們的文件夾下多了很多的東西,.O屬于中間生成的文件,不用理會,重點在于這個HelloOS.bin文件:
[圖片上傳失敗...(image-2e19ea-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
這個HelloOS.bin就好像是刻錄有WIN10操作系統(tǒng)的硬盤,可是沒那么值錢,說不定以后咱們的OS就貴了呢?!
安裝
好了,Hello OS都有了,那么怎么安裝到我們電腦上?
別急,很簡單,慢慢來。
先通過以下命令,找到boot目錄掛載分區(qū):
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">df /boot/ </pre>
我的結果如下:
[圖片上傳失敗...(image-32f3d5-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
也就說我的虛擬機中ubuntu16.04的系統(tǒng)GRUB引導是在硬盤的第一分區(qū)。
然后打開文件夾中的install.md,復制粘貼到****/boot/grub/grub.cfg中,install.md主要是加載我們的Hello OS的啟動項:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">//install.md menuentry 'HelloOS' { insmod part_msdos insmod ext2 set root='hd0,msdos1' #注意boot目錄掛載的分區(qū),這是我機器上的情況 multiboot2 /boot/HelloOS.bin boot } </pre>
注意!df /boot的結果在哪個sda?,set root=‘hd0,msdos?’中的?就填什么。
而后,運行將我們的****Hello OS.bin 文件復制到 /boot/ 目錄下:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">sudo cp HelloOS.bin /boot </pre>
最后重啟計算機,等待中。
[圖片上傳失敗...(image-7cd091-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
失???
emm....第一次失敗了,還是進入了ubuntu。
想起之前安裝windows系統(tǒng)盤的經(jīng)歷,猜測是由于GRUB引導界面時間顯示太短,百度了一下找到這個博客:
https://jingyan.baidu.com/article/6dad50755e35d1a123e36ecc.html
打開sudo gedit /etc/default/grub,修改其中的GRUB_CMDLINE_LINUX_DEFAULT為“text”,并加“#”注釋掉HIDDEN兩行,如下:
[圖片上傳失敗...(image-ef48d0-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
根據(jù)博客所說,需要更新grub配置:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">sudo update-grub </pre>
再次重啟!
[圖片上傳失敗...(image-354f46-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
蕪湖!運行一下看看(選中并enter)
[圖片上傳失敗...(image-b162e3-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
當看到屏幕上顯示的Hello OS的時候,恭喜,你的操作系統(tǒng)已經(jīng)啟動了!
以后你需要做的,就是不斷的完善和拓展你的操作系統(tǒng),使它的功能愈發(fā)強大!
細節(jié)補充
顯卡與printf
在源代碼的main.c中,我們調(diào)用了一個printf:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">`//彭東 @ 2021.01.09
include "vgastr.h"
void main() {
printf("Hello OS!");
return;
}` </pre>
printf()函數(shù)源碼在源文件vgastr.h中:
<pre class="custom" data-tool="markdown.com.cn編輯器" style="margin-top: 10px; margin-bottom: 10px; border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;">//彭東 @ 2021.01.09 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; } </pre>
可能有點看不懂上述代碼是什么意思,這里需要補充一下:我們要在屏幕上顯示字符,就要編程操作顯卡。無論我們 PC 上是什么顯卡,它們都支持一種叫 VESA 的標準,這種標準下有兩種工作模式:字符模式和圖形模式。顯卡們?yōu)榱思嫒葸@種標準,不得不自己提供一種叫 VGABIOS 的固件程序。
字符模式
圖形模式較為復雜,并且我們顯式“Hello OS”只需要簡單的字符模式即可。在字符模式下顯卡把屏幕分成 24 行,每行 80 個字符,把這(24*80)個位置映射到以 0xb8000 地址開始的內(nèi)存中,每兩個字節(jié)對應一個字符,其中一個字節(jié)是字符的 ASCII 碼,另一個字節(jié)為字符的顏色值。如下圖所示:
[圖片上傳失敗...(image-a36603-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>
現(xiàn)在回過頭來看代碼:rintf 函數(shù)直接調(diào)用了 _strwrite 函數(shù),而 _strwrite 函數(shù)正是將字符串里每個字符依次輸入到 0xb8000 地址開始的顯存中,為了跳過字符的顏色信息空間,而 p_strdst 每次加 2。
思考題
在printf 函數(shù)定義,其中有個形式參數(shù)很奇怪,請你思考下:為什么是“…”形式參數(shù),這個形式參數(shù)有什么作用?
答:這其實是C語言可變長參數(shù)的應用。對于x86來說,函數(shù)參數(shù)入棧順序為從右往左,因此,在知道第一個參數(shù)地址之后,我們能夠通過地址偏移獲取其他參數(shù)。
并且在這種方式下,棧頂元素就是printf第一個需要打印的元素,方便顯卡打印。更詳細解釋。
致謝
由衷感謝彭東老師帶來的操作系統(tǒng)精講,截止2021/05/19,課程更新到了第5講,誠意滿滿,收獲滿滿。
但這兩天只是大致地看了一遍,而且由于之前學過操作系統(tǒng),有些不以為意,并沒有真正地去領會其內(nèi)涵,特地寫了這一個學習筆記性質(zhì)的文章,與大家共同學習,一起進步!
https://time.geekbang.org/column/intro/100078401?tab=catalog
https://time.geekbang.org/column/article/369457
課程大綱如下:
[圖片上傳失敗...(image-be185d-1709767645128)]
<figcaption style="margin-top: 5px; text-align: center; color: #888; display: block; font-size: 12px; font-family: PingFangSC-Light;">img</figcaption>