1. 程式人生 > >Windows記憶體分頁機制

Windows記憶體分頁機制

大多數現代的作業系統都支援虛存, 這使得系統上的每個程式都擁有自己的地址空間. 每當程式讀取記憶體時, 都必須指定一個地址. 對於每個程序, 該地址必須轉換為實際的實體記憶體地址. 
例如, 若我們的MzfHips.exe 程式需要的資料在虛擬記憶體地址的0x0041FF10處, 則實際的實體地址可能被對映為0x01EE2F10. 
如下圖:
名稱:  2011-06-11_143032.png檢視次數: 896檔案大小:  4.9 KB
圖1: 虛擬地址轉實體地址示意圖 (圖中的資料僅僅是為了演示)

記憶體地址的轉換主要通過一個稱為頁表目錄的特殊表來完成. Intel x86 CPU將頁表目錄的指標儲存在特殊暫存器CR3中. 該暫存器指向一個包含1024個32位值的陣列, 稱為頁目錄. 每個陣列元素稱為頁目錄項, 它指定了頁表在實體記憶體中的基地址, 還通過狀態位指示該頁表當前是否存在於記憶體中. 從頁表中可以獲得實際的實體地址.

點選圖片以檢視大圖圖片名稱: 2011-06-11_144949.png檢視次數: 938檔案大小: 19.0 KB檔案 ID : 57914
圖2: 在記憶體中尋找頁面

下面我們來看下虛擬地址的組成:
名稱:  2011-06-11_145749.png檢視次數: 895檔案大小:  1.6 KB
由圖可知, 虛擬地址的前10位用來定位頁目錄項, 中間10位用來定位頁表項, 最後12位得到具體實體地址的偏移.

到了這裡, 我們來總結下具體的步驟:
1.  CPU查詢CR3暫存器以找到頁表目錄的基地址
2.  作業系統根據所請求的虛擬地址的前10位(如圖3), 來定位頁目錄項, 從而在記憶體中找到相應的頁表.
3.  頁表根據中間的10位定位該頁相應的實體記憶體首地址
4.  根據虛擬地址的後12位得到具體的實體地址相對於首地址的偏移量.
5.  最後得到的實體地址即包含我們要請求的資料

知道了前面的知識, 下面我們來看下具體的頁目錄項和頁表項中的內容.
由前面我們知道, CR3暫存器指向頁目錄的首地址, 而頁目錄是由最多1024個可能的頁目錄項組成.

下面我們看下頁目錄項的地址組成:
名稱:  2011-06-11_152042.png檢視次數: 892檔案大小:  4.0 KB
當訪問頁目錄項時, 要檢查U位(第2位), 若U位為0, 則意味著正在處理的頁表只能用於核心.
還要檢查W位(第1位), 若W位為0, 則記憶體是隻讀的.
頁目錄項指向整個頁表, 即整個頁面集合, 因此, 頁目錄項中的設定應用於整個記憶體頁範圍.

下面來看下頁表項:
名稱:  2011-06-11_152954.png檢視次數: 896檔案大小:  3.9 KB
當訪問頁表項時, 仍然首先要檢查U位(第2位), 若U位為0, 則只有核心模式的程式才能夠訪問該記憶體頁.
還要檢查W位(第1位), 以判斷讀寫訪問許可權.
最後還要判斷P位(第0位), 若它設定為0, 則當前記憶體頁被換出在磁碟上. 若它設定為1, 則記憶體是常駐, 並且當前是可用的.
在將記憶體頁換出後, 記憶體管理器在成功訪問它之前必須將該頁換入記憶體.


還有最後一個問題就是, 系統上的大多數可執行程式的其實地址都是0x00400000. 多個程序如何能使用同一個虛擬地址, 而不會在實體記憶體中發生衝突?那是因為系統上的每個程序都維護一個獨立的頁目錄, 都擁有自己私有的CR3暫存器的值.
當執行緒發生切換時, 舊執行緒的狀態會被儲存起來. 若當前排程執行的執行緒不屬於剛才的程序, 則當前程序的頁目錄地址會被載入到CR3暫存器中. 頁目錄地址可以在程序的KPROCESS結構中找到.

分析下MmIsAddressValid函式, 加深下對分頁機制的瞭解.(參考combojiang大叔)
下面是ida 看到的ntoskrnl.exe中匯出的MmIsAddressValid。
.text:0040C661 ; BOOLEAN __stdcall MmIsAddressValid(PVOID VirtualAddress)
.text:0040C661                 public MmIsAddressValid
.text:0040C661 MmIsAddressValid proc near              ; CODE XREF: sub_40D65E+Cp
.text:0040C661                                         ; sub_415459:loc_415470p ...
.text:0040C661
.text:0040C661 VirtualAddress  = dword ptr  8
.text:0040C661
.text:0040C661 ; FUNCTION CHUNK AT .text:0041B84E SIZE 00000007 BYTES
.text:0040C661 ; FUNCTION CHUNK AT .text:0044A4F2 SIZE 00000019 BYTES
.text:0040C661
.text:0040C661                 mov     edi, edi
.text:0040C663                 push    ebp
.text:0040C664                 mov     ebp, esp
.text:0040C666                 mov     ecx, [ebp+VirtualAddress] ;取出虛擬地址
.text:0040C669                 mov     eax, ecx
.text:0040C66B                 shr     eax, 14h        ; 右移20位
.text:0040C66E                 mov     edx, 0FFCh      ; 取高10位
.text:0040C673                 and     eax, edx
.text:0040C675                 sub     eax, 3FD00000h  ; 加上0xc0300000
.text:0040C675                                         ; PDE = ((VA >> 22) << 2 )& 0xffc + 0xc0300000
.text:0040C67A                 mov     eax, [eax]
.text:0040C67C                 test    al, 1           ; 頁目錄項的第0位, 即P位
.text:0040C67E                 jz      loc_41B84E
.text:0040C684                 test    al, al
.text:0040C686                 js      short loc_40C6AC ; 判斷page size位
.text:0040C688                 shr     ecx, 0Ah        ; 右移10位
.text:0040C68B                 and     ecx, 3FFFFCh
.text:0040C691                 sub     ecx, 40000000h
.text:0040C697                 mov     eax, ecx        ; PTE = ((VA >> 12) << 2 ) & 0x3FFFC + 0xc0000000
.text:0040C699                 mov     ecx, [eax]      ; ecx = PTE Context
.text:0040C69B                 test    cl, 1           ; 判斷present位
.text:0040C69E                 jz      loc_41B84E
.text:0040C6A4                 test    cl, cl
.text:0040C6A6                 js      loc_44A4F2      ; 判斷page size位
.text:0040C6AC
.text:0040C6AC loc_40C6AC:                             ; CODE XREF: MmIsAddressValid+25j
.text:0040C6AC                                         ; MmIsAddressValid+3DE9Fj
.text:0040C6AC                 mov     al, 1
.text:0040C6AE
.text:0040C6AE loc_40C6AE:                             ; CODE XREF: MmIsAddressValid+F1EFj
.text:0040C6AE                 pop     ebp
.text:0040C6AF                 retn    4
.text:0040C6AF MmIsAddressValid endp
.text:0040C6AF

Ps:上面說的理論是在未開啟PAE模式下的一般情況, 在開啟了PAE的情況下, 可能會有所不同。
*轉載請註明來自看雪論壇@PEdiy.com 

相關推薦

Windows記憶體機制

大多數現代的作業系統都支援虛存, 這使得系統上的每個程式都擁有自己的地址空間. 每當程式讀取記憶體時, 都必須指定一個地址. 對於每個程序, 該地址必須轉換為實際的實體記憶體地址. 例如, 若我們的MzfHips.exe 程式需要的資料在虛擬記憶體地址的0x0041FF10處, 則實際的實體地址可能被對映為0

作業系統實現之記憶體機制.虛擬空間

這裡我們將把頁目錄表放在0x100000處.頁表也挨著頁目錄表放在0x101000處(第二個頁表.當然在此之前應該把實體記憶體給算出來.這裡可以使用bios中斷來獲取實體記憶體%include "boot.inc" section loader vstart=loader_base_addr ;-------

深入詳解保護模式下的記憶體機制

以下是Intel文件中關於分頁機制的詳細描述:IA-32 Memory Models When employing the processor’s memory management facilities, programs do not       — 使用處理器記憶體管理設施時程式不會 directly

【自制作業系統05】開啟記憶體機制

通過前四章的努力,我們成功將控制權轉交給了 loader.asm 這個程式,並且從真實模式跨越到了保護模式。第四章講保護模式的時候我說過,這是我們作業系統的第一個精彩之處。但其實這只是針對之前我們進行的只是無意義的輸出,以及硬碟的載入等工作。但到了這一章,之前一步步的努力進入到了保護模式,也只能說是做了很多苦

作業系統:x86下記憶體機制 (1)

前置知識: 分段的概念(當然手寫過肯定是墜吼的 ##為什麼要分頁 當我們寫程式的時候,總是傾向於把一個完整的程式分成最基本的資料段,程式碼段,棧段。並且普通的分段機制就是在程序所屬的LDT中把每一個段給標識出來。但是在實際運用中,大多數程序不會無限地執行下去。當程序結束之後它佔有的記憶體空間也會被釋放。

記憶體的管理和機制

一、問題提出: 我們經常會使用malloc()以及free()函式進行堆區記憶體申請與釋放。那麼你是否會這樣做: int * p = malloc(0);/*malloc分配了0個位元組嗎,如果是那麼p指向誰呢,是NULL嗎*/ free(p);/*假如malloc分配了0個位元組,p指向了NU

Linux機制之概述--Linux記憶體管理(六)

1 分頁機制 在虛擬記憶體中,頁表是個對映表的概念, 即從程序能理解的線性地址(linear address)對映到儲存器上的實體地址(phisical address). 很顯然,這個頁表是需要常駐記憶體的東西, 以應對頻繁的查詢對映需要(實際上,現代支援VM的處理器都有一個叫TLB的硬體級頁表快取部件

Linux機制機制的演變--Linux記憶體管理(七)

1 頁式管理 1.1 分段機制存在的問題 分段,是指將程式所需要的記憶體空間大小的虛擬空間,通過對映機制對映到某個實體地址空間(對映的操作由硬體完成)。分段對映機制解決了之前作業系統存在的兩個問題: 地址空間沒有隔離 程式執行的地址不確定 不過分段方法存在一個嚴重的問題:記憶體的使用效率

xv6學習筆記 機制記憶體管理

XV6分頁機制、記憶體管理 報告內容 0. mmu.h的閱讀 mmu.h原始碼中給出了XV6虛擬地址的構成,及所代表的含義 mmu.h中還有頁表的相關資訊,每個頁目錄都與1024條記錄,每一個頁表中也有1024條記錄,每一頁的大小4096位元組,也就是4kb。 // Pag

記憶體定址之機制

寫在前面: 分頁機制完成線性地址到實體地址的轉換 80x86 規定分頁機制是可選的。分段和分頁沒有什麼必然聯絡,分段可以說是 Intel 的 CPU 一直保持著的一種機制,而分頁只是保護模式下的一種記憶體管理策略。想開啟分頁機制,CPU必須工作在保護模式,而工

虛擬記憶體的好處及多級機制的原因

眾所周知,在現代計算機系統中都使用了虛擬地址。在一個程式的執行那個過程中,由CPU產生虛擬地址,該虛擬地址經過MMU轉換成實體地址,然後使用該實體地址去訪問記憶體。那麼虛擬地址存在的原因是什麼呢?為何不能由CPU直接產生實體地址呢? 第一,使用虛擬地址可以更加高效的使用實體

Linux核心原始碼分析--記憶體管理(一、機制

        Linux系統中分為幾大模組:程序排程、記憶體管理、程序通訊、檔案系統、網路模組;各個模組之間都有一定的聯絡,就像蜘蛛網一樣,所以這也是為什麼Linux核心那麼難理解,因為不知道從哪裡開始著手去學習。很多人會跟著系統上電啟動 BIOS-->bootse

Linux記憶體地址的分段、機制(上)

在深入學習Linux核心原始碼之前,需要先對Linux執行的硬體基礎有個大概的認識,主要包括CPU中的暫存器和磁碟。 1.i386暫存器和系統指令 在Linux系統中使用的主要包括i386暫存器中的16位標誌暫存器,4個記憶體管理暫存器和4個控制暫存器及

【淺談】x86記憶體管理的分段機制

最近一直在接著作業系統的課程,著手跟著os.dev上的大神的文件寫一個小型的核心,然後前期的東西自己一直在看也沒時間寫Blog,最近做到了記憶體管理這裡,看了Jamesmolloy的文件還有一些os.dev.org上的關於memorymanage的東西,總覺的還是寫點東西吧

Linux的記憶體定址——淺談分段和機制

本文會以80x86架構,linux2.6為例,簡單介紹記憶體的分段和分頁機制。1. 三種記憶體地址關於記憶體地址,首先要了解它有三種,分別是邏輯地址、線性地址和實體地址。把邏輯地址轉換為線性地址是由一個叫做分段單元的硬體電路完成的。同樣地,還有一個叫做分頁單元的硬體電路負責把

記憶體管理與機制

一、問題提出: 我們經常會使用malloc()以及free()函式進行堆區記憶體申請與釋放。那麼你是否會這樣做: int * p = malloc(0);/*malloc分配了0個位元組嗎,如果是那麼p指向誰呢,是NULL嗎*/ free(p);/*假如

Linux機制機制的實現詳解--Linux記憶體管理(八)

[注意] 如果您當前使用的系統並不是linux,或者您的系統中只有一份linux原始碼,而您又期待能夠檢視或者檢索不同版本的linux原始碼 LXR (Linux Cross Reference)是比較流行的linux

Linux記憶體管理解析(一) : 分段與機制

背景 : 在此文章裡會從分頁分段機制去解析Linux記憶體管理系統如何工作的,由於Linux記憶體管理過於複雜而本人能力有限。會盡量將自己總結歸納的部分寫清晰。 從真實模式到保護模式的定址方式的不同 :    16位CPU的定址方式 : 在 8086 CPU 中,提供了兩類暫存器來進行定址,分別為段

80386學習(五) 80386機制與虛擬記憶體

一. 頁式記憶體管理介紹   80386能夠將記憶體分為不同屬性的段,並通過段描述符、段表以及段選擇子等機制,通過段基址和段內偏移量計算出線性地址進行訪問,這一記憶體管理方式被稱為段式記憶體管理。   這裡要介紹的是另一種記憶體管理的方式:80386在開啟了分頁機制後,便能夠將實體記憶體劃分為一個個大小相同且

Linux內存尋址之機制

緩存 itl ans linux 存儲器 apt target tar linux中 http://blog.xiaohansong.com/2015/10/05/Linux內存尋址之分頁機制/ 在上一篇文章Linux內存尋址之分段機制中,我們了解邏輯地址通過分段機制轉換為