在計算機裏,內存地址分爲虛擬內存地址和物理內存地址。linux
數據存放在物理內存中,程序運行時使用的是虛擬內存,並經過虛擬內存地址訪問數據和代碼。程序員
那操做系統是如何將虛擬內存映射爲物理內存地址呢?數據結構
一 虛擬內存佈局佈局
以X86的32位系統位例,在32系統中,系統的虛擬內存地址範圍爲4GB。低2GB給應用程序(Ring3級別)使用。測試
高2GB給系統內核(Ring0)使用。spa
每一個程序都有屬於本身的一個私有2GB虛擬內存空間,以下圖:操作系統
其中的0000-ffff前64KB地址爲NULL地址區域,0x0000ffff-0x7fff0000爲進程空間,3d
0x7fff0000-0x7fffffff爲非法區域,禁止程序訪問,禁止Ring3級別的應用程序不當心訪問到後面2GB的內核內存空間。 指針
① X86的虛擬內存佈局 blog
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位模式時,無論怎樣,寄存器和指令均可以尋址整個線性地址空間,因此根本就不須要再去使用基地址。基址能夠設爲一個統一的值。