詳解作業系統啟動

2021-08-26 14:51:13 字數 4449 閱讀 6511

我們作業系統在啟動的時候,我們的計算機到底什麼?讓我們從馮諾依曼體系說起!其實說白了,整個計算機執行過程就是在取指令和執行指令,如下圖所示,其中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 ...