1. 虛擬地址到物理地址(內存虛擬化)
假設一個堆的基地址為34KB,虛擬地址為4KB,其大小為2KB
當 程序不分段時 ,找到堆中虛擬地值的物理地址很簡單,物理地址 = 基地址 + 虛擬地址
當 程序分段時 ,找到堆中物理地址會復雜一些,物理地址 = 基地址 + (虛擬地址 - 該段的開頭的虛擬地址)
舉個例子,堆中有一個虛擬地址為4200,那麼如果想得到其物理地址,需 34KB + 4200 - 4 KB = 34920
你可能好奇為什麼要這么做,我們來簡單解釋一下:
首先我們先明確,之所以使用虛擬地址是想讓程序以為自己獨占內存,也就是說程序所佔內存是從0 - xxx。虛擬地址是多少,就表示其在第多少個內存空間
當不分段時:整個程序的內存空間連續(無論是程序以為的內存空間還是物理內存都是連續的),所以虛擬地址即表明了其是第幾個內存空間。顯然 物理地址 = 基地址 + 虛擬地址
當分短時:整個程序的內存空間不再連續,每一段都有自己獨特的基地址,但是虛擬地址還是相對於之前只有一個基地址時的值,那麼此時虛擬地址就無法直接表示其在第幾個內存空間了(因為程序以為的連續內存空間映射成的物理內存並不連續)。所以,我們需要虛擬地址相對於每個段自己的基地址的值,要完成這個操作只需要將虛擬地址 - 段開頭的虛擬地址。因此 物理地址 = 基地址 + 虛擬地址 - 段開頭的虛擬地址
2. 邏輯地址轉換物理地址公式
物理地址是明確的、最終用在匯流排上的編號。那麼邏輯地址轉物理地址怎麼轉?我為大家介紹邏輯地址轉物理地址的解決 方法 。希望大家喜歡。
邏輯地址轉換物理地址公式參考如下
1. 物理地址和邏輯地址
物理地址:載入到內存地址寄存器中的地址,內存單元的真正地址。在前端匯流排上傳輸的內存地址都是物理內存地址,編號從0開始一直到可用物理內存的最高端。這些數字被北橋(Nortbridge chip)映射到實際的內存條上。物理地址是明確的、最終用在匯流排上的編號,不必轉換,不必分頁,也沒有特權級檢查(no translation, no paging, no privilege checks)。
邏輯地址:CPU所生成的地址。邏輯地址是內部和編程使用的、並不唯一。例如,你在進行C語言指針編程中,可以讀取指針變數本身值(&操作),實際上這個值就是邏輯地址,它是相對於你當前進程數據段的地址(偏移地址),不和絕對物理地址相干。
(具體步驟 共三步)
1.確定虛擬地址(物理地址)的有效位
例如:假設頁面大小1KB,共32頁。(頁面:邏輯地址 頁框:物理地址)
由32(KB)=32×1024(B) 即等於32×1024 位元組
二進制用多少位能有效表示這么多位元組呢——答是:15位 因為32×1024=2^5×2^10=2^15
2.再次確定邏輯地址頁面位數 你應該知道:邏輯地址=頁號+頁面
還是以上假設,那麼頁面大小為1KB=1024位元組 同樣的方法計算出表示位數:10位
如果給你邏輯地址:0000 1111 1000 0000
那麼由:011+11100000000(相當於 頁號+頁面(10位))推得出頁號011=3
3.根據頁號找出對應的頁框號
由 物理地址=頁框號×頁塊大小(頁塊大小是等於頁面大小的)+頁內位移(即頁面邏輯地址)
根據上面 物理地址=頁框號×1024B + 1110000000 ( 這里的相加是指位置上而言)
例如:110+110=110110(即高地址+低地址)
提問:在一分頁存儲管理系統中,邏輯地址長度為16位,頁面大小為4096B,現有一邏輯地址為2F6AH,且第0、1、2頁依次存放在物理塊5、10、11中,問相應的物理地址為多少?
答:4096B=2^12B
16位定址一共2^16B
分頁存儲。共分的頁:2^16/2^12=2^4=16 共分16頁。
第0頁的地址范圍 0 - FFFH
第1頁的地址范圍 1000H - 1FFFH
第2頁得地址范圍 2000H - 2FFFH
.....
第11頁 B000H - BFFFH
第15頁 F000H - FFFFH
2F6AH=10 1111 0110 1010 在2頁的范圍對應物理塊11
所以物理地址為:
2F6AH - 2000H + B000H = F6AH + B000H= BF6AH
3. 科普 VT、EPT
硬體虛擬化,到底什麼意思,虛擬化什麼東西。VT 其中一個功能虛擬化內存。虛擬化內存什麼意思,比如你的 PC 物理電腦實際上只有 8G 物理內存,但是用了虛擬化內存這個功能後,可以讓你的操作系統(這里就用WINDOWS來代替操作系統)使用超過 8G 的物理內存。可以把你的物理內存隱藏起來讓別人訪問不到,包括操作系統也訪問不到,當然也包括殺毒軟體訪問不到或者訪問到的是你偽造的內存。 進一步講,可以讓你一台PC物理機同時運行多個操作系統,就像VMWARE一樣。多個操作系統之間的物理內存是隔離的。
其實簡單點講就是,1 號操作系統訪問物理內存0X12345678 和 2 號操作系統訪問物理內存 0X1245678 裡面的內容可以不一樣。 這是怎麼做到的? 其實原理挺簡單的。在 WINDOWS 裡面為什麼多個進程訪問同一個地址,但是裡面的內容可以做到不一樣,兩個進程都執行 mov eax,0x40001000,然而取出來的地址會不一樣。
為什麼能做到這點,其實執行這條匯編指令的時候,0x40001000這個地址並不是真正的物理地址,那麼真正的物理地址是什麼呢,真正的物理地址是需要經過MMU(是個硬體)轉換過的。怎麼轉換的呢,http://bbs.pediy.com/showthread.php?t=203391&highlight=物理+理地+地址------大家可以參考這篇文章。
其實 VT EPT 功能就是把物理內存虛擬化了。就是把經過虛擬地址轉換過後的物理地址,還需要再經常 EPT 機制再轉換一次,從而實現,物理地址的虛擬化。
下面說下這是怎麼做到的。INTEL 是怎麼設計的。虛擬地址轉換成物理地址,我們都知道是經過 CR3 所指向一塊內存(可是理解成數組),多大 4096 位元組。(為了便於新手理解其他頁面大小這里暫不考慮)。其實 EPT 機制裡面也有個類似 CR3 的寄存器,就是這個功能,開始轉換地址的首地址 EPTP。這個 EPTP 大家可以簡單理解成 CR3 寄存器。這個虛擬地址到物理地址的轉換是在後台,硬體偷偷在後台的完成的,我們完全感覺不到,當然我們的虛擬機的物理地址轉換成真正的物理地址也是在硬體層次,後台偷偷完成的。我們完全感覺不到。但是怎麼完成的。其實它是通過查表完成的。關鍵就在於這個表,我們把這個表初始化好了,構建好了,CPU 就可以自動後台查找這個表運行轉換功能了。
先看下這個表要怎麼構建,CPU 會怎麼去用這個表,EPT 擴展頁表的功能。我們目前要做的就是把這個 EPT 給構建起來,啥功能都沒有,什麼意思呢,就是物理地址 0X87654321經過這個表轉換之後還是變成 0x87654321(當然想變成什麼地址你可以自己構建)。這里我都是以 64 位系統為例為講的。
看下 INTEL 是怎麼設計,怎麼查找這個表的,首先硬體會從 EPTP 指向的一塊內存開始,查找,這塊內存你可以自己申請好 4096 位元組,把這個起始地址,在 VT 初始化的時候賦值給 VT 的某個欄位。因為是 64 位,所以是 512 項,一項佔八個位元組,相當於 ULONG64 EPTP[512-1] 裡面有 512 項,每一項都是一個地址,這個地址指向另外一塊內存,當然這塊內存也是 4096 位元組,裡面也是 512 項的 64 位地址,就這樣總共下去有四層,數組裡面的每一項指向另外一塊數組的首地址。
反回來再說說,到底是取這個數組的哪一項呢,INTEL 是這么設計的,把 64 位的物理地址分成 5 段,0-11位算一段,12-20位算一段,21-29位算一段,30-38位算一段,39-47位算一段。我們這里先無視0-12位,其他四個段都是9位組成,四個段,剛好對應四層表,就是說,每一段獨立取出來,當作這個數組的頁表的序列。看 INTEL 手冊吧,看書的時候,感覺書上寫的不清楚,論到自己寫的時候,才感覺,要把這個意思表達出來還真不容易,
先把上面 0x87654321 轉換成二進制 ,再把這段二進制分成五段,怎麼分,從低位12,9,9,9,9多餘的位暫且無視,感覺用視頻講解會好很多,這里我試著盡量用文字表達清楚。
000000000 000000010 000111011 001010100 001100100001 分成這樣五段,然後再把這五段二進制分別獨立轉換成 十六 進制0 0x2 0x3B 0x54 0x321 我們先看前面四段,0 2 3B 54 先看這個零代表什麼意思,零就代表上面我們申請的內存頁的第一項,就是EPTP 首地址裡面的第一項的內容。取出裡面的內容,然後看圖
這個地址 fffffa80`01878000就是我申請的 4096 位元組大小的內存的首地址,你會發現只有第一項有內容,其餘的零,因為我們實際物理內存並沒有那麼大,所有隻需要填下第一項就行了。第一項裡面的值也是一個地址,這個地址也是一個頁 4096 位元組大小的首地址,繼續看圖
圖里可以看出也是有 512 項(注意這里存放的都是物理地址,然後無視最後位的數字 7,看作零,7 代表什麼意思我等下再說),每一項又指向一塊 4096 大小的內存的首地址,可以看出我只初始化了 8 項,這 8 項代表 8G,8G是怎麼算出來的,其實這里的每一項代表一個G的物理內存。
先了解虛擬地址怎麼轉換成物理地址的就可以看懂我發的圖了。
4. 操作系統中邏輯地址轉物理地址是什麼
1、確定虛擬地址(物理地址)的有效位。
2、再次確定邏輯地址頁面位數你應該知道:邏輯地址=頁號+頁面。
3、由物理地址=頁框號×頁塊大小(頁塊大小是等於頁面大小的)+頁內位移(即頁面邏輯地址)
4、根據上面物理地址=頁框號×1024B+1110000000。
5、若在一分頁存儲管理系統中,某作業的頁表如下所示。已知頁面大小為1024位元組,試將邏輯地址1011,2148,4000,5012轉化為相應的物理地址。
分析頁式存儲管理的地址結構是一維的,即邏輯地址(或物理地址)只用一個數值即可表示。若給定邏輯地址A,頁面的大小為L,則頁號p和頁內地址d可按照下式求得:
p=int[A/L]d=AmodL
其中,int是取整函數(取數值的整數部分),mod是取余函數(取數值的余數部分)。
5. 分頁,虛擬地址是怎麼轉換成物理地址的
虛擬地址(即圖中的邏輯地址)的高位表示頁號,由計算機硬體將頁號取出,且和頁表寄存器中的頁表始址一起送加法器,就可以得到該頁對應的頁表項的地址,根據此地址到內存讀出對應的塊號,最後將塊號和頁內地址拼接得到對應的物理地址。
6. PGD 功能解析
VPN :virtual page number.
PPN :physical page number.
PTE :page-table entries.
ASID :address space identifier.
PMA :Physical Memory Attributes
PMP :Physical Memory Protection
PGD :Page Global Directory
PUD :Page Upper Directory
PMD :Page Middle Directory
PT :Page Table
TVM :Trap Virtual Memory
4KB 的內存頁大小可能不是最佳的選擇,8KB 或者 16KB 說不定是更好的選擇,但是這是過去在特定場景下做出的權衡。我們在這篇文章中不要過於糾結於 4KB 這個數字,應該更重視決定這個結果的幾個因素,這樣當我們在遇到類似場景時才可以從這些方面考慮當下最佳的選擇,我們在這篇文章中會介紹以下兩個影響內存頁大小的因素,它們分別是:
每個進程能夠看到的都是獨立的虛擬內存空間,虛擬內存空間只是邏輯上的概念,進程仍然需要訪問虛擬內存對應的物理內存,從虛擬內存到物理內存的轉換就需要使用每個進程持有頁表。
在如上圖所示的四層頁表結構中,操作系統會使用最低的 12 位作為頁面的偏移量,剩下的 36 位會分四組分別表示當前層級在上一層中的索引,所有的虛擬地址都可以用上述的多層頁表查找到對應的物理地址 4 。
因為操作系統的虛擬地址空間大小都是一定的,整片虛擬地址空間被均勻分成了 N 個大小相同的內存頁,所以內存頁的大小最終會決定每個進程中頁表項的層級結構和具體數量,虛擬頁的大小越小,單個進程中的頁表項和虛擬頁也就越多。
因為目前的虛擬頁大小為 4096 位元組,所以虛擬地址末尾的 12 位可以表示虛擬頁中的地址,如果虛擬頁的大小降到了 512 位元組,那麼原本的四層頁表結構或者五層頁表結構會變成五層或者六層,這不僅會增加內存訪問的額外開銷,還會增加每個進程中頁表項佔用的內存大小。
PGD中包含若干PUD的地址,PUD中包含若干PMD的地址,PMD中又包含若干PT的地址。每一個頁表項指向一個頁框,頁框就是真正的物理內存頁。
PGD: Page Global Directory
mm_init() ---> fork.c 文件 ,源碼如下:
mm_init() 函數調用 mm_alloc_pgd() 函數與底層物理內存產生關系, mm_alloc_pgd() ---> fork.c 文件
pgd_alloc() ---> paglloc.h 這個函數為當前 pgd 分配一個 page ,並且將當前的 page 的首地址返回,並且將內
核GPG拷貝的當前進程的結構體中。函數中調用了 __get_free_page() ,獲取一個空間的物理頁保存當前進程信息, __get_free_page() 就是Kernel常用的 __get_free_pages() ,這樣子上層進程創建就與底層物理內存產生直接的關系,以上幾個函數源碼如下:
init_mm() ---> init_mm.c 結構體記錄了當前 root table 的所有信息, swapper_pg_dir 是存放PGD 全局信息的全局變數,源碼如下在 init_mm.c 文件中,源碼如下:
這樣一來,每個進程的頁面目錄就分成了兩部分,第一部分為「用戶空間」,用來映射其整個進程空間 (0x0000 0000-0xBFFF FFFF) 即3G位元組的虛擬地址;第二部分為「系統空間」,用來映射 (0xC000 0000-0xFFFF FFFF)1G 位元組的虛擬地址。可以看出 Linux 系統中每個進程的頁面目錄的第二部分是相同的,所以從進程的角度來看,每個進程有 4G 位元組的虛擬空間,較低的 2G 位元組是自己的用戶空間,最高的 2G 位元組則為與所有進程以及內核共享的系統空間。每個進程有它自己的 PGD( Page Global Directory) ,它是一個物理頁,並包含一個 pgd_t 數組。
An Sv32 virtual address is partitioned into a virtual page number (VPN) and page offset, as shown in
Figure 4.15.
satp寄存器的組成:
虛擬地址轉換為物理地址轉換過程如下:
每一個應用程序都有自己的Page Global Directory(PGD),其保存物理地址的頁幀,在<asm/page.h>中定義了pgd_t 結構體數組,不同的架構有不同的PGD載入方式。
A virtual address va is translated into a physical address pa as follows:
當虛擬地址沒有映射物理地址,最典型就是用戶態 Malloc 一段虛擬地址後, Linux 並沒有為這段虛擬地址分配物理地址,而是當用寫這段虛擬地址時, Linux Kernel 發生 PageFault 才會為這段虛擬地址映射物理內存,大概的過程就是這樣,但是其中 Linux Kernel 產生缺頁異常到映射物理的過程則是非常復雜的一個過程,其中涉及到很重要的一個函數就是缺頁中斷服務函數,在 RISC-V 中叫 do_page_fault() 在 arch/risv-v/mm/fault.c 文件中定義了該函數。
do_page_fault() 函數實現如下:
7. 現代CPU如何自動把虛擬地址轉換成物理地址的硬體電路
虛擬內存是一個由存放在磁碟上的N個連續的位元組大小的單元組成的數組。
每個位元組都有一個唯一的地址,就是虛擬地址。通常,虛擬地址由頁號和偏移量組成,頁號就是抽象的虛擬頁的編號,偏移量用於計算實際的物理地址。
虛擬地址和物理地址的關系。進程雖然使用虛擬地址,但是用數據時還是要到實際的物理地址去取數據。這就存在一個虛擬地址到物理地址的轉化運算,這是由CPU晶元上一個叫做內存管理單元(MMU)的專用硬體來實現的。
通常,物理地址=頁號*頁大小+頁內偏移量。虛擬定址CPU通過虛擬地址來訪問主存,訪問內存使用的物理地址,MMU通過將虛擬地址進行翻譯,轉化為物理地址,然後再用這個物理地址去訪問內存數據。
8. 通過虛擬地址計算物理地址 求過程
你打的太多了,有點亂,只說下地址轉換問題:
1.虛擬地址:虛擬地址是以"段寄存器:偏移地址"形式存在的,例如--0542:24521360
2.線性地址:它是由分段部件把虛擬地址轉化而來的.
3.物理地址:即真實存在的地址,由處理器的地址引腳尋找到的地址.
虛擬地址---->線性地址:
段寄存器是一個16位的寄存器,其中第0和1位控制著將要訪問段的特權級,第2位說明是在gdt還是ldt尋找地址.高13位作為一個索引值,總共8192個索引.假設段寄存器-0000
0000
0000
1011(000b),那麼我們可以知道rpl=3(特權級為3);ti=0,從gdt中選擇段描述符;index=1,即將要索引的段描述符在gdt中的順序號為1,由於一個段描述符佔8個位元組,所以其索引到的地址為"gdt的高32位+1*8".這也就是為什麼gdt48位,留最低的16位作為限長的原因(8192*8=64k).
找到了段描述符,然後就是從段描述符中找出該段的位置了.段描述符是個8位元組的內存空間,由於結構復雜,無法構圖,省略段描述符的結構.我們只要知道在裡面規定了該段的基址,限長,還有屬性等等.找出基址後,再加上虛擬地址的偏址,就形成了32位的線性地址.由於偏址是32位的,所以該段獨享4g的虛擬地址空間.
線性地址----->物理地址
該部分是由分頁部件通過3級查找完成的.此時,我們把線性地址分為3段:0-11位(c)位元組索引,12-21位(b)頁表索引,22-31位(a)頁目錄索引.我們把頁表描述符和頁描述符通稱為頁表項,頁表項佔4個位元組,總共佔4kb大小.先以cr3為基址,以(a*4)為索引值,定址頁目錄描述符.然後再以頁目錄地址的高20位地址為基址,以(b*4)為索引值,定址頁描述符.再以頁描述符的高20位地址為基址,以c為偏移地址,相加得到物理地址.
從上可以看到頁的大小是4kb,即一項任務cpu只調用該任務所佔內存空間的4kb大小.有利於減少內存佔用.
以上大體就是這樣的,其中分頁部件的轉換相當復雜,不是三言兩語就能說明白的.還有pentium之後,分頁部件又採用了4mb的頁面,線性地址採用2級定址.才開啟pae功能後,又形成了4級定址.然後再結合後面的內存保護,i/o保護,任務保護及特權級的變換,形成了保護模式的大部分內容.
太復雜了,我也不是十分會.寫的有些亂,但願你能明白些.
9. 關於內存管理和地址轉換的小小小小小總結
因為在ipad上畫圖比較好操作,這篇筆記就直接上傳手寫版了。把線性地址到物理地址部分的轉換理了一下,以後有補充會做更新。
四級頁表的作用主要就是地址映射,將邏輯地址映射到物理地址。
ARM MMU的地址轉換過程實際上更加復雜,通過兩級頁表實現,轉換方式有兩大類共四種情況,具體的可以看這篇博客 https://blog.csdn.net/sinat_41104353/article/details/82778822
已知系統使用IA-32分頁,現知道一個虛擬地址0x10036270,需要將該虛擬地址轉換為物理地址。若已知CR3寄存器中的值為0x7401000,轉化的過程如下:
1. 虛擬地址為0x10036270(00010000 00000011 01100010 01110000)
22-31bit為PDI值(00 0100 0000),12-21bit為PTI值(00 0011 0110 ),0-11bit為地址偏移(010 0111 0000)
2.頁目錄項PDE的地址=PDI×4+PDB(CR3)=0x40×4+0x740100=0x7401100
3.知道PDE物理地址後即可知道該物理地址中存儲的值,比如假設該物理地址存儲的值為0x28cf9067。PTE的值由PDE值的12-31bit及虛擬地址的12-21bit構成(0-11bit根據12bit填充為0),可得到PTE的物理地址=0x28cf9058
4.假設該物理地址中的值為0x182a7071,物理地址的值由PTE值的12-31bit及偏移地址構成。
最終得到物理地址=0x28cf9000+0x270=0x28cf9270。
以上為IA-32分頁虛擬地址轉物理地址的過程。
關於虛擬地址到物理地址的轉換
由於在內存中存儲的一般是虛擬地址,而在物理內存中地址定位的一定是物理地址,因此計算虛擬地址(線性地址)到物理地址的映射關系是內存分析的關鍵。
虛擬地址到物理地址的映射計算需要使用到一個基本規則: 在同一個虛擬地址頁面上的內容,也在同一個物理頁面。
比如,在物理內存管理中,頁的大小一般為4KB、2MB、4MB,都大於或等於0x1000(4KB)。根據上述的規則,虛擬地址0xffdff000-0xffdfffff就應該映射到同一個物理頁面上。而計算系統的頁目錄基地址是計算內存映射的關鍵,如果在0xffdff000-0xffdfffff中找到指向系統頁目錄基地址的指針就會解決地址映射的問題。
在上部分的筆記中能看到,CR3寄存器是非常重要的一個寄存器,它記錄的是頁目錄基地址(或頁目錄指針基地址、或PLM4基地址),如果能得到CR3寄存器的內容,那麼就有可能得到現成的頁目錄基地址。
這里以《內存取證原理與實踐》的例子,先大概描述一下 利用CR3的虛擬地址找到其物理地址的方法 。
以64位win7操作系統為例,_KPRCB 的結構成員ProcessorState是一個_KPRROCESSOR_STATE結構,起始地址為0xfffff80045eff80+0x40,在0x0處是SpecialRegister成員,偏移0x010處就是CR3寄存器,它的虛擬地址為0xfffff80045eff80+0x40+0x10。
而根據上述提到的基本規則我們可以知道,它和0xfffff800045efe00在同一個頁面中,那麼所以它的物理地址= 0xFFFFF800045EFE00的物理地址+0x180(這兩個地址的差值)+0x40+0x10。
關於頁的分頁方式和頁的大小則由以下過程確定:
1. 根據CR3寄存器的內容找到它指向的物理地址。
2. 判斷該地址處的第一個位元組,如果不是0x01則跳轉至第三步,否則表明其使用了PAE模式,從這個地址開始的8byte是頁目錄指針。根據待轉換的虛擬地址的第31~30 bit選擇頁目錄指針。例如,如果待轉換的地址是0x8054c2b8(10000000 01010100 11000010 10111000),則頁目錄指針表的第三項(二進制10)為指向頁目錄的指針,根據這個指針可找到頁目錄基地址。
根據頁目錄基地址和虛擬地址的第21~20bit確定待轉換虛擬地址對應的頁目錄項。例如,如果待轉換的地址是ox8054c2b8,則第21~29bit是000000010(0x02),則從頁目錄基地址加上8×2開始的8個位元組就是所找的頁目錄項。
3. 判斷該地址處的第一個位元組最高位,如果是「1」,則表明使用的大頁模式;如果是「0」,則表明它指向頁表。