內存尋址:邏輯地址到物理地址的轉化


 

 

  在計算機里,內存地址分為虛擬內存地址和物理內存地址。

數據存放在物理內存中,程序運行時使用的是虛擬內存,並通過虛擬內存地址訪問數據和代碼。

那操作系統是如何將虛擬內存映射為物理內存地址呢?

一 虛擬內存布局

  以X86的32位系統位例,在32系統中,系統的虛擬內存地址范圍為4GB。低2GB給應用程序(Ring3級別)使用。

高2GB給系統內核(Ring0)使用。

  每個程序都有屬於自己的一個私有2GB虛擬內存空間,如下圖:

其中的0000-ffff前64KB地址為NULL地址區域,0x0000ffff-0x7fff0000為進程空間,

0x7fff0000-0x7fffffff為非法區域,禁止程序訪問,禁止Ring3級別的應用程序不小心訪問到后面2GB的內核內存空間。  

 

      

  ① X86的虛擬內存布局 

X86支持32位尋址,因此可以支持2的32次方即4GB的虛擬內存空間。虛擬地址再通過頁表機制映射到物理地址以便存取物理內存中的數據和指令。

PS: 也可以通過PAE模式將尋址空間擴大到64GB,PAE是物理地址擴展,在4GB的虛擬地址空間中,Windows系統的

 4GB內存主要分為2GB的內核空間和2GB的應用層空間。

  ② X64的虛擬內存布局

X64(AMD64)的內存布局與X86的內存布局類似,不同的地方在於各自空間的范圍和大小不同,同時X64下還存在一些空洞。

在X64系統,理論上支持最大2的64次方字節尋址空間,空間太大了根本用不完。所以實際上X64系統一般只支持到40多位

Windows支持44位最大尋址空間位16TB,Linux支持48位最大尋址空間位256TB。這些TB的空間並不都是可用的,存在所謂的空洞。

二 邏輯地址轉化為物理地址

  程序在執行時,傳遞給CPU的地址是邏輯地址,邏輯地址由二部分組成,一部分是段選擇符(比如cs和ds等段寄存器的值),

另一部分為有效地址(即偏移地址,比如eip寄存器的值)。

  邏輯地址必須經過轉化變成線性地址,線性地址再經過一次映射轉為物理地址,才能訪問真正的物理內存。

  

①邏輯地址轉化為線性地址

邏輯地址是以“段地址:偏移地址”的形式存在的。

段寄存器是一個16位的寄存器,第0位和第1位控制着將要訪問段的特權級,第2位說明是在GDT還是LDT尋找地址,

高13位作為一個索引值,總共8192個索引。如下圖所示,通過寄存器里的索引,可以從段描述符表里找到段的基址。

然后用段的基址加上段內的偏移量,就得到了對應的線性地址。

    

②線性地址轉化位物理地址

  由第①步得到線性地址,線性地址分為三部分:頁目錄索引,頁表索引,字節偏移索引。

如下圖所示,通過頁目錄索引的CR3寄存器指定的頁目錄基址之和,可以查詢到對應的頁表基址。

再通過頁表索引和頁表基址之和,可以得到對應的頁框地址,頁框地址再加上頁內字節偏移,就得到了物理地址。

    

 

從C源代碼經過編譯器編譯,鏈接器鏈接生成可執行文件之后,接下來程序是怎么執行起來的呢?

接下來,操作系統會將可執行文件加載入內存,CPU將從程序的第一個指令開始執行。在理解CPU是如何執行程序之前,先來看看CPU的構造。CPU主要由運算器、控制器、寄存器組和內部總線等構成。 ALU:運算器是計算機中執行各種算術和邏輯運算操作的部件。運算器由算術邏輯單元(ALU,Arithmetic Logical Unit)、累加器、狀態寄存器、通用寄存器組等組成。算術邏輯運算單元(ALU)的基本功能為加、減、乘、除四則運算,與、或、非、異或等邏輯操作,以及移位、求補等操作。計算機運行時,運算器的操作和操作種類由控制器決定。運算器處理的數據來自存儲器;處理后的結果數據通常送回存儲器,或暫時寄存在運算器中。 CU:控制器是計算機的指揮中心,負責決定執行程序的順序,給出執行指令時機器各部件需要的操作控制命令。由程序計數器、指令寄存器、指令譯碼器、時序產生器和操作控制器組成,它是發布命令的“決策機構”,即完成協調和指揮整個計算機系統的操作。控制器從內存中取出一條指令,並指出下一條指令在內存中位置,對指令進行譯碼或測試,並產生相應的操作控制信號,以便啟動規定的動作,指揮並控制CPU、內存和輸入/輸出設備之間數據流動的方向。 寄存器組用於在指令執行過后存放操作數和中間數據,由運算器完成指令所規定的運算及操作。

1)系統總線

CPU的系統總線包括控制總線,數據總線,地址總線。
數據總線用於傳送數據信息。數據總線是雙向總線,即它既可以把CPU的數據傳送到存儲器或I/O接口等其他部件,也可以將其他部件的數據傳送到CPU。 地址總線是專門用來傳送地址的,由於地址只能從CPU傳向外部存儲器或I/O端口,所以地址總線總是單向的,這與數據總線不同。地址總線的位數決定了CPU可直接尋址的內存空間大小,比如8位微機的地址總線為16位,則其最大可尋址空間為216=64KB,16位微型機的地址總線為20位,其可尋址空間為220=1MB。一般來說,若地址總線為n位,則可尋址空間為2n字節。有的系統中,數據總線和地址總線是復用的,即總線在某些時刻出現的信號表示數據而另一些時刻表示地址,而有的系統則是分開的。 控制總線用來傳送控制信號。控制信號中,有的是微處理器送往存儲器和I/O接口電路的,如讀/寫,中斷響應信號等; 也有是其他部件反饋給CPU的,比如:中斷申請、復位、總線請求、設備就緒等。因此,控制總線的傳送方向由具體控制信號而定, 一般是雙向的,控制總線的位數要根據系統的實際控制需要而定。實際上控制總線的具體情況主要取決於CPU。

2)寄存器

CPU的一個重要組成部分就是它的寄存器。 計算機體系結構中常用到的寄存器包括以下幾類寄存器(以32位X86系統為例):
a) 通用寄存器:EAX,EBX,ECX,EDX
b) 源變址目標變址寄存器:ESI,EDI
c) 棧相關寄存器:SS,ESP,EBP
d) 代碼段寄存器,程序指令寄存器:CS,IP
e) 數據段寄存器:DS(常與ESI寄存器結合使用)
f) 附加段寄存器:ES(常與EDI寄存器集合使用)
g) Flag標志寄存器:
ZF 零標志,零標志ZF用來反映運算結果是否為0。如果運算結果為0,則其值為1,否則其值為0;
AF 輔助進位標志,運算過程中第三位有進位值,置AF=1,否則,AF=0;
PF 奇偶標志,當結果操作數中偶數個"1",置PF=1,否則,PF=0;
SF 符號標志,當結果為負時,SF=1;否則,SF=0。溢出時情形例外;
CF 進位標志,最高有效位產生進位值,例如,執行加法指令時,MSB(最高位)有進位,置CF=1;否則,CF=0;
OF 溢出標志,若操作數結果超出了機器能表示的范圍,則產生溢出,置OF=1,否則,OF=0。

在64位系統中,寄存器的表示方法為:
通用寄存器:rax, rbx, rcx, rdx
棧寄存器:rsp, rbp
傳遞參數的寄存器:rdi, rsi, (rdx, rcx,) r8, r9(arguments)
Scratch寄存器:(rbx,) r12, r13, r14, r15(scratch),即可以隨時改寫的寄存器

那么,CPU是如何一條條執行程序的指令的呢?如下面CPU執行指令圖所示, 首先,CPU中的CS寄存器指向了程序被加載內存之后所在代碼段的基址,而IP寄存器指向了下一條程序要執行的指令。 CS中的段基址加上IP寄存器中的值,形成一個線性地址,這個線性地址經過轉換,形成物理地址,然后通過地址總線, 在對應的內存地址獲得對應的一條指令,再把對應的指令通過數據總線傳輸到CPU的指令緩沖器中, 然后由指令緩沖器傳給指令執行控制器,執行對應的指令。

內存的尋址模式討論


邏輯地址,線性地址,物理地址

1.邏輯地址是編譯器生成的,我們使用在linux環境下,使用C語言指針時,指針的值就是邏輯地址。對於每個進程而言,他們都有一樣的進程地址空間,類似的邏輯地址,甚至很可能相同。邏輯地址由段地址+段內偏移組成
2.線性地址是由分段機制將邏輯地址轉化而來的,如果沒有分段機制作用,那么程序的邏輯地址就是線性地址了。
3.物理地址是CPU在地址總線上發出的電平信號,要得到物理地址,必須要將邏輯地址經過分段,分頁等機制轉化而來。 

x86體系結構下,使用的較多的內存尋址模型主要有三種: 
1. 實模式扁平模型 real mode flat model 
2. 實模式分段模型 real mode segment model 
3. 保護模式扁平模型 protected mode flat model

實模式和保護模式相對,實模式運行於20位地址總線,保護模式則啟用了32位地址總線,地址使用的是虛擬地址,引入了描述符表;雖然二者都引入了段這樣一個概念,但是實模式的段是64KB固定大小,只有16個不同的段,CS,DS等存儲的是段的序號。保護模式則引入了GDT和LDT段描述符表的數據結構來定義每個段。 

扁平模型和分段模型相對,區別在於程序的線性地址是共享一個地址空間還是需要分成多個段,即為多個程序是同時運行在同一個CS,DS的范圍內還是每個程序都擁有自己的CS,DS:也就是說前者(flat)指令的邏輯地址要形成線性地址,不需要切換CS,DS;后者的邏輯地址,必須要經過段選擇子去查找段描述符,切換CS,DS,才能形成線性地址。 

實模式分段模型 real mode segment model 

在實模式里,20位地址總線,16位的寄存器無法表示,一個基址寄存器+一個段寄存器聯合起來則可以表示更大的一個地址空間。於是發明了這種段寄存器左移4位+基址寄存器用以間接尋址。 20根地址線,表示 0x00000 - 0xfffff這個范圍的地址(即1M) 而寄存器16位,還有4位怎么辦?於是8086CPU將1MB的存儲器空間分成許多邏輯段,每個段最大限制為64KB(為了能讓16位寄存器尋址,2^20=2^10*2^10=2^10*2^6*2^4==16*64K), 段地址就是邏輯段在主存中的起始位置。為了能用16位寄存器表示段地址,8086規定段地址必須是模16地址,即為xxxx0H形式,省略低4位0,段地址就可以用16位數據表示,它通常被保存在16位的段寄存器中。存單元距離段起始位置的偏移量簡稱偏移地址,由於限定每段不超過64KB,所以偏移地址也可以用16位數據表示。物理地址:在1M字節的存儲器里,每一個存儲單元都有一個唯一的20位地址,稱為該存儲單元的物理地址,把段地址左移4位(因為段地址低4位都是零)再加上偏移地址就形成物理地址。Seg<<4+Offset 對於 8086/8088 運行在實模式的程序,其實就是運行在實模式分段模型中。對於不同的程序,有不同的CS,DS值,每個程序的段起始地址都不同。對於這樣的程序而言,偏移地址16位的特性決定了每個段只有64KB大小。 

實模式扁平模型 real mode flat model 

該模式只有在386及更高的處理器中才能出現。80386的實模式,就是指CPU可用的地址線只有20位,能尋址0~1MB的地址空間。注意:80386的實模式並不等同於8086/8088的實模式,后者的實模式其實就是實模式分段模型。扁平模型,意味着我們這里不使用任何的分段寄存器。(盡管也使用了CS,DS,只是不用程序員去顯示地為該寄存器賦值,jmp指令時就已經將CS, DS設置好了) 

保護模式扁平模型 protected mode flat model 

Linux, Window XP/7采用的內存尋址模型,Linux中,段主要分為4種,即為內核代碼段,內核數據段,用戶代碼段,用戶數據段。 對於內核代碼段和數據段而言,CS,DS的值是0xC00000000,而用戶代碼和數據段的CS,DS的值是0x00000000 當CPU運行於32位模式時,不管怎樣,寄存器和指令都可以尋址整個線性地址空間,所以根本就不需要再去使用基地址。基址可以設為一個統一的值。 


免責聲明!

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



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