我們作業系統在啟動的時候,我們的計算機到底什麼?讓我們從馮諾依曼體系說起!其實說白了,整個計算機執行過程就是在取指令和執行指令,如下圖所示,其中pc(程式計數器)指向一條指令,計算機將其從儲存器取出,然後放到運算器中執行,也就是如圖所示,最終得到ax的值是0。
開機一瞬間軟體上做了什麼?
在我們開機的那一瞬間,系統會基本完成上電動作,同時,此時cpu會將段暫存器cs設定為0xffff,ip暫存器設定為0x0000,由於這個時候是實模式,那麼此時cs:ip=0xfff0,也就是我們bios rom的起始位址。對於rom來說,此處存放的程式是不會因為斷電而丟失的。通過bios這段程式,主要做兩個事兒,一是初始化部分硬體;而是將0磁軌0扇區的內容讀入到0x7c00處,我們知道0磁軌0扇區存放的就是我們計算機的引導程式了,接下來就會將cs設定為0x07c0,ip設定為0x0000,也就是讓cs:ip指向引導程式。下面,就要進入到我們的boot階段了。
引導扇區**bootsect.s
接下來我們利用**來說明boot做了啥。其中省略了大部分**,只說說主幹。
_start:
mov ax,#bootseg //bootseg=0x07c0
mov ds,ax
mov ax,#initseg //initseg=0x9000
mov es,ax
mov cx,#256
mub si,si //ds:si=0x7c00
mub di,di //es:di=0x90000
repmovw //有cx=256,那麼就是移動256次,每次是兩個位元組,最終由0x7c00來到0x90000
jmpi go,initseg //此處為段間跳轉,此時cs=initseg,ip=go
從上面跳轉到go這段,下面看看go的**。
go:
mov ax,cs //cs=0x9000
mov ds,ax
mov es,ax
mov ss,ax
mov sp,#0xff00 //此段**主要是設定ds、es、ss、sp,sp即為棧的設定
後面就是進行關鍵的setup模組的載入了。
load_setup:
mov dx,#0x0000 //dh為磁頭號,dl為驅動器號
mov cx,#0x0002 //ch為磁軌號,cl為開始扇區號=2,也就是setup模組是以
mov bx,#0x0200 //偏移位址0x200
mov ax,#0x200+setuplen //此處setuplen=4,即為setup的大小,4個扇區,ah=0x02
int 0x13 //bios讀磁碟扇區的中斷
jnc ok_load_setup
mov dx,#0x0000 //置0
mov ax,#0x0000 //置0
int 0x13 //復位磁碟
jmp load_setup //要進入setup部分了!
最後一段**就是將setup模組載入到以0x90200為起點的地方。
setup.s模組
下面進入到setup.s部分看看這段**做了什麼!
start:
mov ax,#initseg //initseg=0x9000,也就是bootsect.s的段位址
mov ds,ax
mov ah,#0x03 //這句話用於獲取游標的位置
xor bh,bh
int 0x10 //輸出dh=行號,dl=列號
mov [0],dx //儲存游標的行號和列號到0x90000,共占用2個位元組
mov ah,#0x88
int 0x15 //利用bios中斷0x15來獲取系統所含擴充套件記憶體大小
mov [2],ax //儲存在記憶體0x90002處,
//下面這段**的作用是將system模組移動到0x0000開始的地方,其中移動的大小為512kb
mov ax,#0x0000
clddo_move:
mov es,ax
add ax,#0x1000
cmp ax,#0x9000 //當ax=0x9000時結束
jz end_move
mov ds,ax
sub di,di
sub si,si
mov cx,#0x8000 //重複0x8000次
rep //ds:si --> es:di
movsw //每次移動2bit
jmp do_move
mov ax,#0x0001
mov cr0,ax //此句是從實模式到保護模式切換的關鍵,其中cr0的第0位如果是0,就是實模式,如果是1,就是保護模式,在上面那句中,ax=0x0001,此時賦給cr0,那麼第0位就是1了,自此,進入保護模式
jmpi 0,8
什麼是實模式?什麼是保護模式?在我看來,實模式就是16位的環境,保護模式就是32位的環境,至少從彙編**來說是這樣的,而最大的區別體現在兩者定址方式的不通。
對於16位的環境,只能定址64kb的位址空間,所以通過位址線將其定址方式擴充套件到20位,即1m,那麼在實際的定址中,實際位址=基位址*16+偏移位址,但是到了32位的時候,定址方式就不同了,在setup.s這段**中,還設定了gdt(全域性描述符)\idt,分別用於保護模式下的位址翻譯和中斷處理。
那麼對於setup.s最後一句,jmpi 0,8,其中cs=8,ip=0,而cs的值就是用來查詢gdt表項的,最終會跳轉到0x0000處,也就是system模組載入的起始位址,後面就開始system模組的執行了。
所以在保護模式下,就是根據cs查表+ip來得到需要訪問指令的位址。
system模組之head.s
startup_32:
movl $0x10,%eax
movl %eax,%ds
movl %eax,%es
mov %eax,%fs
mov %eax,%gs
lss_stack_start,%esp //設定棧
call setup_idt
call setup_gdt
xorl %eax,%eax
incl %eax
movl %eax,0x0000
cmpl %eax,0x100000
jp lb
jmp after_page_tables //設定頁表
after_page_tables:
pushl $0
pushl $0
pushl $0
pushl $l6
pushl $_main
jmp set_paging
l6:
jmp l6
set_paging:
......
ret
對於從after_page_tables開始的**,我們看到連續將三個0壓入了棧中,其實這三個0就是mian函式中的envp、argv、argc,只是此處的main並沒有使用,僅僅保留了傳統main的方式和命名,而main函式就是各種初始化,包括記憶體、中斷、裝置、cpu等的初始化。
在壓棧過程中,最後壓入的是main,然後去執行set_paging部分的**,當執行到最後是ret指令,即彈棧。從而進入main函式,在main函式後面是l6,這是乙個死迴圈,也就是說main函式會一直執行下去,除非宕機或者斷電。
main函式做了啥?
void main(void)
}
main函式就是各種初始化,初始化完成後會到使用者空間去執行一些應用。然後看看men_init()函式。
void mem_init(long start_mem,long end_mem)
至此,作業系統的啟動就完成了,接下來就可以到使用者空間去執行程式了。總結來說就是將作業系統讀入到記憶體,完成各種初始化。下面補充一張硬體存放上述各個塊的示意圖。
作業系統啟動順序
在這裡以x86的處理器為例 機器在啟動的時候會執行第一條指令。這條指令會去執行bios,將控制權交給bios。bios完成硬體的質檢,然後將bootloader從硬碟讀到記憶體中,執行bootloader,並將控制權交給bootloader bootloader負責使能保護模式 建立段機制以及載入作...
作業系統啟動過程
當我們按下開機鍵後,作業系統究竟是如何跑起來的?這個過程詳細說來很複雜。這裡只簡單描述一下。當機器剛從生產線上下線的時候,裡面沒有作業系統,稱之為裸機。裸機什麼事都幹不了,於是需要裝上作業系統。機器中固化了乙個用於讀取磁碟或者其他裝置的程式,於是當你在啟動時按下f2鍵,就會執行這個程式安裝作業系統。...
作業系統啟動過程
計算機的儲存器分為 大容量儲存器 通常為硬碟 和 主儲存器 即 記憶體 操作 系統 如 windows unix linux mac os 安裝在大容量儲存器上,而主儲存器又分為兩部分 能夠永久儲存資料的rom read only memory 和易失性儲存器部分 即在關機後資料全部丟失 bios ...