32位Linux系統虛擬地址映射

  IA32體系即Intel32位體系架構,也被稱爲i386、X86-32或X86。在Intel公司1985年推出的80386微處理器中首先使用。用以取代之前的X86-16位架構,包括8086、80186、80286芯片。談到這兒,就不得不說說X86架構的發展歷史。
  Intel 8086是由Intel於1978年所設計的16位微處理器芯片,是x86架構的鼻祖。8086是16位CPU,數據總線16條,地址總線20條,能保存的地址的大小是2^20=1M。8086增加了4個段寄存器用來保存各內存段的起始地址,這4個段寄存器分別爲CS(代碼段寄存器)、DS(數據段寄存器)、SS(堆棧段寄存器)、ES(擴展段寄存器)。由於地址總線共20條,段地址有20位,但是段寄存器只有16位,不能保存20位的地址。因此,將內存的大小劃分爲16的倍數(此時還沒有操作系統,直接操作的是物理內存)。每塊內存起始地址的後四位都爲0,段寄存器只保存地址的高16位。正如前面所講,8086時,地址總線最多隻能保存1M的地址空間。此時,物理內存=段基址+邏輯地址/偏移量。此時若要取數據,就需要找到物理內存,方法爲從DS寄存器中取值,左移4位,就得到了真正的起始地址(DS<<4),再加上變量在該內存段上的偏移量(IP寄存器保存了當前數據在內存段上的偏移量),就得到了數據的物理內存。DS<<4 + IP = 數據的地址。DS<<4 稱爲段基址,稱IP寄存器保存的數爲邏輯地址、偏移量或者偏移地址。8086時,操作系統不存在,無任何權限控制信息,通過驅動接口可以任意更改段寄存器的值,無任何保護措施。稱這種模式爲實模式。實模式下,物理地址=DS<<4 + IP。
  在實模式下,最大訪問的物理內存是1M。CPU通電後,強制進入實模式,CPU只能訪問1M的物理內存。通過Linux內核源碼也可以看到,Linux內核image都是從0X10000開始加載的,虛擬地址是從0Xc0100000開始加載,因爲前1M保存的是驅動代碼、顯卡的緩存等等。
  實模式下訪問內存是極其不安全的,對於CPU的寄存器沒有任何有效性的保護。其實,在訪問一塊內存時,不僅僅需要知道內存的起始地址,還需要知道內存段的大小、內存的訪問權限(可讀、可寫或者可執行)。這麼多信息不可能全都放在段寄存器中,即使80386已經是32位CPU了,CS、DS、SS段寄存器還是16位的(同一體系,必須兼容)。爲了存儲內存段的其他信息,從80386開始,增加了GDTRLDTR兩個寄存器,GDTR中保存的是GDT的地址,但是LDTR中保存的是LDT的地址在GDT的索引。GDT是全局的段描述符表,LDT是局部的段描述符表,這兩個段描述符表也是80386新增的。可以把他們理解爲一個結構體數組,每個元素(段描述符表項)佔8字節。後面會專門介紹這個段描述符表項。有了段描述符表保存內存段信息後,段寄存器就空出來了,轉而保存段描述符表項的索引。其結構如下:

這裏寫圖片描述

  尋址時,從寄存器中取出低地址第3位的數字,判斷內存段的信息保存在GDT還是LDT中,然後以寄存器的高13位數字爲索引,在GDT表中找到段描述符表項。段描述符表項結構如下:

這裏寫圖片描述

   該結構是一個段描述符表項,佔8字節,共64位。其中,有32位保存內存段的起始地址,20位保存內存段的長度,可以保存的內存段的長度爲2^20=1M。如果G位爲0,長度的單位是byte,內存段的總長度是1M;如果G位爲1,長度的單位是頁面(4K),內存段的總長度是1M*4K=4G。80386開始有了操作系統,將虛擬地址映射爲物理地址是由MMU(Memory Management Unit)完成的,這種模式稱爲保護模式。
   保護模式下,內存分段的地址映射尋址過程是:從寄存器中取高13位,以此爲索引,在GDT中找到相應的段描述符表項,從段描述符中得到內存段的地址,再將內存段的長度和偏移量做比較,如果偏移量>內存段的長度,則說明數據可能被修改,結束該進程的運行;如果偏移量合法,段基址加上偏移量就是線性地址。因此,保護模式下,內存分段的地址映射的尋址方式爲:GDT[DS>>3].BaseAddr + IP = 線性地址。
   得到線性地址後,需要檢查內是否開啓內存分頁機制。若沒有開啓內存分頁機制,線性地址就是物理地址。若開啓了內存分頁機制,此時的線性地址是虛擬地址,要通過多級頁表映射才能得到物理地址。
   怎樣判斷內核是否開啓內存分頁機制呢?不得不提到4個寄存器了,它們分別是CR0、CR2、CR3、CR4寄存器。CR0的最高位PG位若爲0,未開啓分頁機制;若爲1,開啓分頁機制。CR2保存發生缺頁異常時的虛擬地址。CR3保存當前進程的頁目錄的起始地址。CR4的PAE位若爲0,未開啓物理地址擴展;若爲1,開啓了物理地址擴展。通過CR0寄存器的PG位的值就可以判斷內核是否開啓分頁機制。
   32位系統需要二級頁面映射,36位系統需要三級頁面映射,64位系統需要四級頁面映射。下面就以32位系統的二級頁面映射爲例,具體說一下內存分頁機制下,如何進行物理內存尋址。
   上面說到過,開啓內存分頁機制後,通過內存分段地址映射的方式得到的線性地址是虛擬地址,還需要對虛擬地址進行內存分頁地址映射才能得到物理地址。虛擬地址共32位,其中高10位表示的是頁目錄的下標,次高10位表示頁表的下標,低12位表示物理頁面上的偏移量。具體劃分如下圖示:

這裏寫圖片描述
 
  高10位可以表示2^10=1024個索引,次高10位也可以表示2^10=1024個索引,低12位可以表示2^12=4K的偏移(物理頁面,1頁面=4K,設計並非偶然)。因此,頁目錄(Page Directory)和頁表(Page Table)可以看作含有1024個元素的數組。每個元素佔4字節,所以頁目錄、頁表的大小爲4K,物理頁面的大小也是4K,頁表和物理頁面的起始地址都是4K的倍數,後16位都爲0,所以頁目錄只保存頁表地址的高20位,頁表只要保存物理頁面地址的高20位。剩下的12位保存的內容稍後會談到。
這會兒,內存分頁的地址映射過程即將浮出水面了。從CR3寄存器中拿到當前進程頁目錄的地址,根據高10位保存的頁目錄下標定位到頁目錄中的Entry項,Entry項的高20位保存的是頁表地址的高20位,將後16位補上0後,就是頁表的完整地址。再根據次高10位,找到頁表的Entry項,Entry項的高20位保存的是物理頁面的高20位,將後16位補上0後,就是物理頁面的完整地址。最後,物理頁面的地址+低12位的偏移量,就得到了物理地址。
  還有一個小尾巴沒有解決。Entry的高20位保存了地址的高20位,那低16位保存的是什麼呢?存權限!有沒有分配、能不能訪問等等信息。其中最重要也是最有用的一位是Page Table的Entry項的最低位,present位。若present位=0,表示頁表項所對應的物理頁面在交換分區中;若present位=1,表示頁表項所對應的物理頁面就在物理內存中。根據LRU最近最久未使用算法,將物理內存中最近最久未使用的頁面交換到交換分區中。程序剛開始運行時,會訪問數據、指令等等,發出的第一個邏輯地址就要發生地址映射,但剛開始執行的時候沒有分配任何物理內存,發生缺頁異常,操作系統分配物理頁面後,重新進行映射。由於程序運行有“局部性”原理,頁面一經分配,其他大多數時候訪問的都是這個頁面。所以,一般只有第一次映射時會發生缺頁異常,其他情況下發生缺頁異常的概率非常非常小。
  到這兒,內存的段頁式管理就解釋完畢了。下面再附一張圖,幫助大家更好的理解這個過程。
  這裏寫圖片描述
 
    如果你能很好的解釋這張圖,你對以上知識已經掌握了。

發佈了37 篇原創文章 · 獲贊 31 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章