天天看點

從開機加電到執行main函數之前的過程

分三步完成:

1.啟動BIOS,準備實模式下的中斷向量表和中斷服務程式

2.從啟動盤加載作業系統程式到記憶體,加載作業系統程式的工作是利用第一步中準備的中斷服務程式實作的

3.為執行32位的main函數做過渡工作。

注:什麼是實模式/保護模式?

1.1  0xFFFF0

CPU硬體邏輯設計為加電瞬間強行将CS的值置為0xFFFF,IP的值置為0x0000,CS:IP為0xFFFF0,0xFFFF0指向BIOS位址範圍。

BIOS的入口位址就是0xFFFF0.

BIOS程式在記憶體最開始的位置(0x00000)用1KB的記憶體空間(0x00000~0x003FF)建構中斷向量表,并在緊挨着它的位置用256位元組的記憶體空間建構BIOS資料區(0x00400~0x004FF),在大約56KB以後的位置(0x0E05B)加載了8KB左右的與中斷向量表相應的若幹中斷服務程式。

中斷向量表中有256個中斷向量,每個中斷向量占4個位元組,其中兩個位元組是CS的值,兩個位元組是IP的值,每個中斷向量都指向一個具體的中斷服務程式。

1.2  加載作業系統核心程式并為保護模式做準備

分三批逐次加載核心代碼:

第一批由BIOS中斷int 0x19h把第一扇區bootsect的内容加載到記憶體

第二批和第三批在bootsect的指揮下,分别把其後的四個扇區和随後的240個扇區的内容加載到記憶體

1.2.1  加載第一部分代碼----引導程式(bootsect)

計算機硬體體系結構設計與BIOS聯手操作,會讓CPU接收到一個int 19h中斷,CPU接收到這個中斷後,會立即在中斷向量表中找到此中斷。中斷向量把CPU指向0x0E6F2,即中斷服務程式的入口位址。作用就是把軟碟的第一個扇區中的程式(bootsect 512B)加載到記憶體的0x07C00處。

1.2.2  加載第二部分代碼-----setup

1.  Bootsect對記憶體的規劃

要加載的setup程式的扇區數(SETUPLEN)

被加載到的位置(SETUPSEG)

啟動扇區被BIOS加載的位置(BOOTSEG)

将要移動到的新位置(INITSEG)

核心被加載的位置(SYSSEG)

核心的末尾位置(SYSEND)

根檔案系統裝置号(ROOT_DEV)

2.複制bootsect

Bootsect将自身從記憶體0x07C00(BOOTSEG)處複制到記憶體(0x90000INITSEG)處

為什麼要複制?

由于“兩頭約定”和“定位識别”的作用,bootsect在開始時被迫加載到0x07C00

此時CPU的段寄存器(CS)指向0x07C0(BOOTSEG)

Jmpi go,INITSEG

Go:mov  ax,cs

執行這個跳轉後,CS值變為0x9000,IP的值為從0x9000(INITSEG)到go:mov ax,cs這一行對應指令的偏移。

用CS的值0x9000來把資料段寄存器(DS)、附加段寄存器(ES)、棧基址寄存器(SS)設定成與代碼段寄存器CS相同的位置,并将棧頂指針SP指向偏移位址為0xFF00處。

SS和SP聯合使用構成了棧資料在記憶體中的位置值,為棧操作打下基礎。

3.  将setup程式加載到記憶體中

借助BIOS提供的int 0x13h中斷服務程式,把指定扇區的代碼加載到記憶體的指定位置,需将指定的扇區和加載的記憶體。setup.s對應的程式加載至記憶體的SETUPSEG(0x90200)處

1.2.3加載第三部分代碼-----system子產品

借助BIOS提供的int 0x13h中斷服務程式,加載至記憶體的SYSSEG(0x10000)處往後的120KB空間中。

Jmpi 0,SETUPSEG跳轉到0x90200處,即setup程式加載的位置。Setup開始執行。

它做的第一件事就是利用BIOS提供的中斷服務程式從裝置上提取核心運作所需的機器系統資料,包括光标位置和顯示頁面等資料,并分别從中斷向量0x41和0x46向量值所指的記憶體位址處擷取硬碟參數表1和硬碟參數表2,把它們存放在0x9000:0x0080和0x9000:0x0090處。

這些機器系統資料被加載到記憶體的0x90000~0x901FC位置。

1.3   開始向32位模式轉變,為mian函數的調用做準備

1.3.1  關中斷并将system移動到記憶體位址起始位置0x00000

關中斷:将CPU的标志寄存器(EFLAGS)中的中斷允許标志(IF)置0

将位于0x10000的核心程式拷貝至記憶體起始位址0x00000處,廢除BIOS中斷向量表

1.3.2  ??設定中斷描述符表IDTR和全局描述符表GDTR

1.3.3  打開A20,實作32位尋址

線性尋址達到4GB

1.3.4  為在保護模式下執行head.s做準備

為了建立保護模式下的中斷機制,setup程式将對可程式設計中斷控制器8259A進行重新程式設計。

Setup程式将CR0寄存器的第0位(PE)置1,即設定處理器的工作方式為保護模式。

1.3.5 head.s開始執行

先将head.s彙編成目标代碼,将用C語言編寫的核心程式編譯成目标代碼,然後連結成system子產品。System子產品裡面,就有核心程式,又有head程式,兩者是緊挨着。Head程式在記憶體中占有25KB+184B的空間。System子產品加載到記憶體後,setup将system子產品複制到0x00000位置,由于head程式在system的前面,head程式就在0x00000這個位置。

         head程式除了做一些調用main的準備工作之外,用程式自身的代碼在程式自身所在的記憶體空間建立了核心分頁機制,在0x000000的位置建立了頁目錄表、頁表、緩沖區、GDT、IDT,并将head程式已經執行過的代碼所占的記憶體空間覆寫。

head.s程式被編譯後,會被連接配接成system子產品的最前面位置,它被setup.s加載到記憶體絕對位址0處開始的地方,并執行。此時Linux核心已經完全在保護模式下運作。head.s的主要功能包括:

1. 設定核心堆棧;

2. 設定中斷描述符表idt;

3. 設定全局描述符表gdt;

4. 設定頁目錄表和頁表;

5. 将/init/main.c程式的入口位址預先壓入堆棧中,并在随後利用傳回指令彈出該位址,去執行main()程式。

插入:

1.  記憶體管理寄存器

4個記憶體管理寄存器(GDTR、LDTR、IDTR和TR),用于指定記憶體分段管理所用系統表的基位址。

GDTR、LDTR、IDTR、TR都是段基址寄存器,這些段含有分段機制的重要資訊表。GDTR、IDTR和LDTR用于尋址存放描述表的段。TR用于尋址一個特殊的任務狀态段(TSS)。TSS包含着目前執行任務的重要資訊。

2.      段的定義

分段機制就是把虛拟位址空間中的虛拟記憶體組織成一些長度可變的稱為段的記憶體單元。

80386虛拟位址空間中的虛拟位址(邏輯位址)由一個段部分和一個偏移部分構成。段是虛拟位址到線性位址轉換機制的基礎。

         每個段由以下參數定義:

(1) 段基位址(Base Address):指定段線上性位址空間中的開始位址。基位址是線性位址,對應于段中偏移0處。

(2) 段限長(Limit):是虛拟位址空間中段内最大可用偏移位置,定義了段的長度。

(3)段屬性(Attribute):指定段的特性。例如該段是否可讀、可寫或可作為一個程式執行,段的特權級等。

注:IP/EIP:指令指針寄存器,存在于CPU中,記錄将要執行的指令在代碼段内的偏移位址,與CS組合即将要執行的指令的記憶體位址。實模式為絕對位址,指令指針為16位,即:IP;保護模式為線性位址,指令指針為32位,即EIP。

  CS:代碼段寄存器,存在于CPU中,指向CPU目前執行代碼在記憶體中所在的區域

  GDT:它是系統中唯一存放段寄存器内容(段描述符)的數組,配合程式進行保護模式下的段尋址。可了解為所有程序的總目錄表,其中存放着每一個任務(task)局部描述符表位址和任務狀态段位址,用于完成程序中各段的尋址、現場保護與現場恢複。

 GDTR:可以存放在記憶體的任何位置,當程式提供段寄存器引用一個段描述符時,需要取得GDT的入口,GDTR所辨別的即為此入口。在作業系統對GDT的初始化完成後,可以用LGDT指令将GDT基位址加載至GDTR

        IDT:儲存保護模式下所有中斷服務程式的入口位址

  IDTR:IDT基位址寄存器,儲存IDT的起始位址

疑問??:

BIOS在ROM中怎麼與RAM聯系起來了?

從setup怎麼過渡到head?

3. 邏輯位址到線性位址的轉換

一個邏輯位址由兩部分組成,段辨別符:段内偏移量。段辨別符是由一個16位長的字段組成,稱為段選擇符。前13位為索引号,後3位包括硬體細節:

Base字段,描述了一個段地開始位置的線性位址。

全局的段描述符,放在“全局段描述符表(GDT)中

局部的,就放在“局部描述符表(LDT)“中。由段選擇符中的T1字段表示。=0,表示用GDT,=1,表示用LDT

邏輯位址[段選擇符:段内偏移位址]

1. 看段選擇符的T1是0還是1,知道目前要轉換的是GDT中的段,還是LDT中的段,找到段描述符表

2.   根據段選擇符中的錢13位,索引到對應的段描述符,就有了Base(16-31位)基位址

在Linux下,邏輯位址與線性位址總是一緻

4. 線性位址到實體位址的轉換

CPU的頁式記憶體管理單元,負責把一個線性位址,翻譯為一個實體位址。

線性位址被分為以固定長度為單元的組,稱為頁(page)。

例如一個32位的機器,線性位址最大可為4G,可以用4KB為一個頁來劃分,整個線性位址就被劃分為一個 tatol_page[2^20]的大數組,共有2的20個次方個頁。這個大數組我們稱之為頁目錄。目錄中的每一個目錄項,就是一個位址——對應的頁的地 址。

另一類“頁”,我們稱之為實體頁,或者是頁框、頁桢的。是分頁單元把所有的實體記憶體也劃分為固定長度的管理機關,它的長度一般與記憶體頁是一一對應的。

這裡注意到,這個total_page數組有2^20個成員,每個成員是一個位址(32位機,一個位址也就是4位元組),那麼要單單要表示這麼一個數組,就要占去4MB的記憶體空間。為了節省空間,引入了一個二級管理模式的機器來組織分頁單元。

如上圖,

1、分頁單元中,頁目錄是唯一的,它的位址放在CPU的cr3寄存器中,是進行位址轉換的開始點。

2、每一個活動的程序,因為都有其獨立的對應的虛似記憶體(頁目錄也是唯一的),那麼它也對應了一個獨立的頁目錄位址。——運作一個程序,需要将它的頁目錄位址放到cr3寄存器中,将它儲存下來。

3、每一個32位的線性位址被劃分為三部份,面目錄索引(10位):頁表索引(10位):偏移(12位)

依據以下步驟進行轉換:

1、從cr3中取出程序的頁目錄位址(作業系統負責在排程程序的時候,把這個位址裝入對應寄存器);

2、根據線性位址前十位,在數組中,找到對應的索引項,因為引入了二級管理模式,頁目錄中的項,不再是頁的位址,而是一個頁表的位址。(又引入了一個數組),頁的位址被放到頁表中去了。

3、根據線性位址的中間十位,在頁表(也是數組)中找到頁的起始位址;

4、将頁的起始位址與線性位址中最後12位相加,得到最終我們想要的葫蘆;

繼續閱讀