MIT 6.828 JOS學習筆記6. Appendix 1: 實模式(real mode)與保護模式(protected mode)


  在我們閱讀boot loader代碼時,遇到了兩個非常重要的概念,實模式(real mode)和保護模式(protected mode)。

  首先我們要知道這兩種模式都是CPU的工作模式,實模式是早期CPU運行的工作模式,而保護模式則是現代CPU運行的模式。

  但是為什么現代CPU在運行boot loader時仍舊要先進入實模式呢?就是為了實現軟件的向后兼容性不得已才這樣的。

  下面我們分別看下這兩種工作模式的基本原理。

實模式(real mode)

  實模式出現於早期8088CPU時期。當時由於CPU的性能有限,一共只有20位地址線(所以地址空間只有1MB),以及8個16位的通用寄存器,以及4個16位的段寄存器。所以為了能夠通過這些16位的寄存器去構成20位的主存地址,必須采取一種特殊的方式。當某個指令想要訪問某個內存地址時,它通常需要用下面的這種格式來表示:

  (段基址:段偏移量)

  其中第一個字段是段基址,它的值是由段寄存器提供的。段寄存器有4種,%cs,%ds,%ss,%es。具體這個指令采用哪個段寄存器是由這個指令的類型來決定的。比如要取指令就是采用%cs寄存器,要讀取或寫入數據就是%ds寄存器,如果要對堆棧操作就是%ss寄存器。總之,不管什么指令,都會有一個段寄存器提供一個16位的段基址。

  第二字段是段內偏移量,代表你要訪問的這個內存地址距離這個段基址的偏移。它的值就是由通用寄存器來提供的,所以也是16位。那么問題來了,兩個16位的值如何組合成一個20位的地址呢?這里采用的方式是把段寄存器所提供的段基址先向左移4位。這樣就變成了一個20位的值,然后再與段偏移量相加。所以算法如下:

  物理地址 = 段基址<<4 + 段內偏移

  所以假設 %cs中的值是0xff00,%ax = 0x0110。則(%cs:%ax)這個地址對應的真實物理地址是 0xff00<<4 + 0x0110 = 0xff110。

  上面就是實模式訪問內存地址的原理。

 

保護模式(protected mode)

  但是隨着CPU的發展,CPU的地址線的個數也從原來的20根變為現在的32根,所以可以訪問的內存空間也從1MB變為現在4GB,寄存器的位數也變為32位。所以實模式下的內存地址計算方式就已經不再適合了。所以就引入了現在的保護模式,實現更大空間的,更靈活的內存訪問。

  在介紹保護模式的工作原理之前,我們必須先清楚以下幾個容易混淆的概念。邏輯地址(logical address),虛擬地址(virtual address),線性地址(linear address),物理地址(physical address)。

  我們都知道,如今在編寫程序時,程序時運行在虛擬地址空間下的,也就是說,在程序員編寫程序時指令中出現的地址並不一定時這個程序在內存中運行時真正要訪問的內存地址。這樣做的目的就是為了能夠讓程序員在編程時不需要直接操作真實地址,因為當它在真實運行時,內存中各個程序的分布情況是不可能在你編寫程序時就知道的。所以這個程序的這條指令到底要訪問哪個內存單元是由操作系統來確定的。所以這就是一個從虛擬地址(virtual address)到真實主存中的物理地址(physical address)的轉換。

  那么邏輯地址(logical address)又是什么呢?根據上面一段文字我們知道,程序員編寫時看到的是虛擬地址,但是並不是說程序員是直接把這個虛擬地址寫到指令中的。它是由邏輯地址推導得到的。所以指令中真實出現的是邏輯地址。一個邏輯地址是由兩部分組成的,一個段選擇子(segment selector),一個段內偏移量(offset),通常被寫作segment:offset。而且采用哪個段選擇子通常也是在指令中隱含的,程序員通常只需要指明段內偏移量。然后分段管理機構(segmentation hardware)將會把這個邏輯地址轉換為線性地址(linear address)。如果該機器沒有采用分頁機制(paging hardware)的話,此時linear address就是最后的主存物理地址。但是如果機器中還有分頁設備的話,比如內存大小實際只有1G,但是根據前面我們知道可訪問的空間有4G。所以此時還需要分頁機構(paging hardware)把這個線性地址轉換為最終的真實物理地址。所以可見虛擬地址和線性地址的含義是差不多的。我們可以再下圖中看到我們上面敘述的地址轉換過程。在boot loader中,並沒有開啟分頁機構。所以計算出來的線性地址就是真實要訪問的主存地址。

  

  那么在保護模式下,我們是如何通過segment:offset最終得到物理地址的呢?

  首先,在計算機中存在兩個表,GDT,LDT。它們兩個其實是同類型的表,前者叫做全局段描述符表,后者叫做本地段描述符表。他們都是用來存放關於某個運行在內存中的程序的分段信息的。比如某個程序的代碼段是從哪里開始,有多大;數據段又是從哪里開始,有多大。GDT表是全局可見的,也就是說每一個運行在內存中的程序都能看到這個表。所以操作系統內核程序的段信息就存在這里面。還有一個LDT表,這個表是每一個在內存中的程序都包含的,里面指明了每一個程序的段信息。我們可以看一下這兩個表的結構,如下圖所示:

  

  我們從圖中可以看到,無論是GDT,還是LDT。每一個表項都包括三個字段:

  Base : 32位,代表這個程序的這個段的基地址。

  Limit : 20位,代表這個程序的這個段的大小。

  Flags :12位,代表這個程序的這個段的訪問權限。

  當程序中給出邏輯地址 segment:offset時,他並不是像實模式那樣,用segment的值作為段基址。而是把這個segment的值作為一個selector,代表這個段的段表項在GDT/LDT表的索引。比如你當前要訪問的地址是segment:offset = 0x01:0x0000ffff,此時由於每個段表項的長度為8,所以此時應該取出地址8處的段表項。然后首先根據Flags字段來判斷是否可以訪問這個段的內容,這樣做是為了能夠實現進程間地址的保護。如果能訪問,則把Base字段的內容取出,直接與offset相加,就得到線性地址(linear address)了。之后就是要根據是否有分頁機構來進行地址轉換了。

  比如當前Base字段的值是0x00f0000,則最后線性地址的值為0x00f0ffff。

  如上所述就是保護模式下,內存地址的計算方法。

  

  綜述,通過上面的敘述可見,保護模式還是要比實模式的工作方式靈活許多,可以在以下幾個方面看出來:

  1. 實模式下段基地址必須是16的整數倍,保護模式下段基地址可以是4GB空間內的任意一個地址。

  2. 實模式下段的長度是65536B,但是保護模式下段的長度也是可以達到4GB的。

  3. 保護模式下可以對內存的訪問多加一層保護,但是實模式沒有。

  

  有什么問題,大家可以給我發郵件~

    zzqwf12345@163.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM