我是如何学习写一个操作系统(二):操作系统的启动之Bootloader
前言
今天本來的任務(wù)看書和把之前寫的FragileOS整理一下,但是到現(xiàn)在還在摸魚,書也只看一點(diǎn)。后來整理了一下寫這個(gè)系列的思路,原本的目的是對(duì)操作系統(tǒng)原理性的學(xué)習(xí)和對(duì)之前寫的一個(gè)玩具型操作系統(tǒng)的回顧,就是想對(duì)操作系統(tǒng)的知識(shí)的輪廓能有一個(gè)了解,現(xiàn)在想來想減少對(duì)之前寫的系統(tǒng)的回顧,畢竟也只有2000多行,但是還是要有對(duì)整個(gè)思路的展現(xiàn)。然后增加對(duì)Linux 0.12源碼的一些學(xué)習(xí)。所以離標(biāo)題可能比較遠(yuǎn)了一點(diǎn),但是就這樣吧
什么是操作系統(tǒng)
原本這一節(jié)是寫計(jì)算機(jī)系統(tǒng)和操作系統(tǒng)概述的,但是寫到一半覺得太水就刪了。就總結(jié)幾句,后面用到什么就補(bǔ)什么。計(jì)算機(jī)系統(tǒng)的概述應(yīng)該屬于計(jì)算機(jī)組成原理的內(nèi)容,這倆部分也是《操作系統(tǒng):精髓和設(shè)計(jì)原理》的第一二章。但是覺得如果對(duì)于想學(xué)習(xí)操作系統(tǒng)內(nèi)部的代碼的話,換成匯編的內(nèi)容會(huì)更好。
進(jìn)入正題,操作系統(tǒng)是什么
對(duì)于計(jì)算機(jī)來說最根本的運(yùn)行方式,就是取指執(zhí)行
對(duì)于在屏幕上輸出Hello,World!的過程,首先CPU拿到內(nèi)存中的指令,這些指令是通知CPU把存在某個(gè)內(nèi)存中的'H''E''L'等移動(dòng)到顯存位置,這樣在屏幕上就可以看到這些字符了。這就是計(jì)算機(jī)最原始的運(yùn)行方式
而操作系統(tǒng)就是對(duì)硬件層面的抽象,讓我們不用在直面硬件,如果想要再次在屏幕輸出字符,只要直接調(diào)用操作系統(tǒng)的write(windows下的好像是這個(gè)名),C語言中的printf下就是一個(gè)系統(tǒng)調(diào)用
當(dāng)然操作系統(tǒng)絕對(duì)是比想象中的龐大的多,操作系統(tǒng)還對(duì)內(nèi)存、終端、磁盤、網(wǎng)絡(luò)和文件等等進(jìn)行管理,光windows 2000應(yīng)該就有3000多萬行的代碼了。當(dāng)然有簡(jiǎn)陋的內(nèi)存、進(jìn)程管理和文件系統(tǒng)的玩具型內(nèi)核,只要幾千行代碼就可以完成了。
操作系統(tǒng)的啟動(dòng)
對(duì)于X86架構(gòu)的計(jì)算機(jī),開機(jī)時(shí)一共做這幾件事
開機(jī)時(shí)的CS = 0xFFFF, IP = 0x0000
這時(shí)候的CPU處理實(shí)模式,也就是尋址的方式是CS:IP (實(shí)模式和保護(hù)模式屬于CPU的工作模式,其中比較大的區(qū)別就是尋址的方式)
尋址0xFFFF0
檢查硬件設(shè)備,像鍵盤顯示器之類的
將磁盤0磁道0扇區(qū)讀入0x7c00處
會(huì)從這里讀入512字節(jié),也就是傳說中的引導(dǎo)程序,這里放著計(jì)算機(jī)執(zhí)行的第一段代碼
設(shè)置cs = 0x7c00 ip = 0x0000
FragileOS/boot
這個(gè)是我之前寫的FragileOS的boot,采用的是Intel匯編格式
主要的邏輯就是:
- 先加載到0x7c00位置
- 進(jìn)行初始化操作
- 調(diào)用CPU提供的中斷來讀取數(shù)據(jù)
- 讀取完畢后直接跳到內(nèi)核的起始位置,也就是引導(dǎo)結(jié)束了
(部分代碼)
org 0x7c00; ;加載到內(nèi)存0x7c00處LoadAddr EQU 08000h ;內(nèi)核的內(nèi)存地址 BufferAddr EQU 7E0h ;讀取扇區(qū)的時(shí)候進(jìn)行的緩存BaseOfStack EQU 07c00hentry: mov ax, 0 ;進(jìn)行寄存器的初始化操作mov ss, axmov ds, axmov ax, BufferAddrmov es, ax ;ES:BX 數(shù)據(jù)存儲(chǔ)緩沖區(qū),指示扇區(qū)加載后放置的地址mov ax, 0mov ss, axmov sp, BaseOfStackmov di, axmov si, axmov BX, 0 ;ES:BX 數(shù)據(jù)存儲(chǔ)緩沖區(qū)mov CH, 1 ;CH 用來存儲(chǔ)柱面號(hào)mov DH, 0 ;DH 用來存儲(chǔ)磁頭號(hào)mov CL, 0 ;CL 用來存儲(chǔ)扇區(qū)號(hào)read_floppy: ;每次都把扇區(qū)寫入緩存地址07E00處cmp byte [load_count], 0 ;比較load_count地址處的值,如果=0就跳轉(zhuǎn)到begin_loadje begin_loadmov bx, 0inc CLmov AH, 0x02 ;AH = 02 表示要做的是讀盤操作mov AL, 1 ;AL 表示要練習(xí)讀取幾個(gè)扇區(qū)mov DL, 0 ;驅(qū)動(dòng)器編號(hào),一般我們只有一個(gè)軟盤驅(qū)動(dòng)器,所以寫死int 0x13 ;調(diào)用BIOS中斷實(shí)現(xiàn)磁盤讀取功能jc finLinux 0.12/boot
Linux 0.12的boot自然比上面的復(fù)雜的多,Linux采用的boot的匯編是as86格式的,其余的匯編采用的都是AT&T
Linux 0.12下的boot一共有三個(gè)文件:
- bootsect
- head
- setup
(代碼太長(zhǎng)不全部貼了,有需要的可以私信我)
bootsect
bootsect的主要作用就是把自己移動(dòng)到0x90000處執(zhí)行,然后再加載setup模塊 (也就是setup.s)到bootsect的后面,再把system模塊加載到0x10000處,這個(gè)也就是內(nèi)核的主要部分
bootsect的開頭是一些常量的定義
SETUPLEN = 4 ! nr of setup-sectors BOOTSEG = 0x07c0 ! original address of boot-sector INITSEG = 0x9000 ! we move boot here - out of the way SETUPSEG = 0x9020 ! setup starts here SYSSEG = 0x1000 ! system loaded at 0x10000 (65536). ENDSEG = SYSSEG + SYSSIZE ! where to stop loading_start先設(shè)置好目的地址和源地址
ds:si和es:di
然后執(zhí)行rep指令
rep指令是重復(fù)的意思,它以cx寄存器的值為判斷,如果cx的值為0就停止
movw指令
開始從[si]處移動(dòng)cx個(gè)字到[di]處,這里也就是一共移動(dòng)了256個(gè)字,512字節(jié)
最后跳轉(zhuǎn)到0x9000開始執(zhí)行
- 現(xiàn)在的這些代碼都是在0x90000后的
- 先重新設(shè)置段寄存器和棧指針
這一部分和我之前的一樣,就是調(diào)用中斷來讀取磁盤內(nèi)容,只是Linux讀取的是在第二扇區(qū)的setup模塊
如果失敗就重新設(shè)置驅(qū)動(dòng)器然后跳回重新讀取
成功就跳到ok_load_setup
ok_load_setup是設(shè)置根文件系統(tǒng)設(shè)備的,并且讀入SYSTEM模塊 (內(nèi)核的主要部分)到0x10000處,結(jié)尾是跳到SETUP模塊
小結(jié)
一個(gè)簡(jiǎn)單的boot引導(dǎo)程序,顧名思義就是把做一些引導(dǎo)工作的,進(jìn)行一些初始化設(shè)置再讀入真正的內(nèi)核部分,進(jìn)入OS。
其實(shí)Linux 0.12一個(gè)完整的boot應(yīng)該還包括setup.s用來完成OS啟動(dòng)前最后的設(shè)置 (進(jìn)入保護(hù)模式等),head.s則是進(jìn)入之后的設(shè)置。但是因?yàn)檫@兩部分包含了一些其它重要概念,所以打算再下一篇寫。
轉(zhuǎn)載于:https://www.cnblogs.com/secoding/p/11405968.html
總結(jié)
以上是生活随笔為你收集整理的我是如何学习写一个操作系统(二):操作系统的启动之Bootloader的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Silver Cow Party POJ
- 下一篇: 我是如何学习写一个操作系统(三):操作系