線性地址轉實體地址

  邏輯地址,需要轉換成線性地址,再經過MMUCPU中的記憶體管理單元轉換成實體地址才能夠被訪問到。那麼線性地址怎麼轉實體地址?今天小編整理了的相關資料,希望對大家有幫助。

  

  線性地址Linear Address 是邏輯地址到實體地址變換之間的中間層。程式程式碼會產生邏輯地址,或說是段中的偏移地址,加上相應段的基地址就生成了一個線性地址。如果啟用了分頁機制,那麼線性地址能再經變換以產生一個實體地址。若沒有啟用分頁機制,那麼線性地址直接就是實體地址。Intel 80386的線性地址空間容量為4G2的32次方即32根地址匯流排定址。

  詳細說下虛擬地址的轉換。

  首先,來看下核心為一個程序建立頁目錄和頁表以及線性空間的函式。這個函式原型是:

  VOID

  MiInitMachineDependent

  IN PLOADER_PARAMETER_BLOCK LoaderBlock

  

  位於init386.c的762行,這個函式太長了,挑選部分如下:

  PointerPte = MiGetPdeAddress PDE_BASE;

  PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE PointerPte;

  CurrentProcess = PsGetCurrentProcess ;

  #if defined_X86PAE_

  PrototypePte.u.Soft.PageFileHigh = MI_PTE_LOOKUP_NEEDED;

  _asm {

  mov eax, cr3

  mov DirBase, eax

  }

  //

  // Note cr3 must be 32-byte aligned.

  //

  ASSERT DirBase & 0x1f == 0;

  //

  // Initialize the PaeTop for this process right away.

  //

  RtlCopyMemory PVOID &MiSystemPaeVa,

  PVOID KSEG0_BASE | DirBase,

  sizeof MiSystemPaeVa;

  CurrentProcess->PaeTop = &MiSystemPaeVa;

  #else

  DirBase = MI_GET_PAGE_FRAME_FROM_PTE PointerPte << PAGE_SHIFT;

  #endif

  CurrentProcess->Pcb.DirectoryTableBase[0] = DirBase;

  KeSweepDcache FALSE;

  //

  // Unmap the low 2Gb of memory.

  //

  PointerPde = MiGetPdeAddress 0;

  LastPte = MiGetPdeAddress KSEG0_BASE;

  MiZeroMemoryPte PointerPde, LastPte - PointerPde;

  第一句:PointerPte = MiGetPdeAddress PDE_BASE;這句獲得一個頁表的指標。

  PDE_BASE是頁目錄的基址,這個巨集的定義如下:i386.h 1968行

  #define PDE_BASE_X86 0xc0300000

  #define PDE_BASE_X86PAE 0xc0600000

  #define PTE_TOP_X86 0xC03FFFFF

  #define PDE_TOP_X86 0xC0300FFF

  #define PTE_TOP_X86PAE 0xC07FFFFF

  #define PDE_TOP_X86PAE 0xC0603FFF

  #if !defined _X86PAE_

  #define PDE_BASE PDE_BASE_X86

  #define PTE_TOP PTE_TOP_X86

  #define PDE_TOP PDE_TOP_X86

  #else

  #define PDE_BASE PDE_BASE_X86PAE

  #define PTE_TOP PTE_TOP_X86PAE

  #define PDE_TOP PDE_TOP_X86PAE

  #endif

  #define PTE_BASE 0xc0000000

  可以看出,如果CPU支援PAE或者開啟了PAE奔騰往後都支援,預設開啟,那麼一個程序頁目錄的基址為0xc0600000,這個在dbg除錯的時候也可以看到,如

  kd> !pte 0000

  VA 00000000 虛擬地址

  PDE at C0600000 PTE at C0000000

  contains 0000000002B40067 contains 0000000000000000

  pfn 2b40 ---DA--UWEV not valid

  再來看這個MiGetPdeAddress ,他是個巨集,定義如下:mi386.h 1889行

  #define MiGetPdeAddressva PMMPTEULONGva >> 22 << 2 + PDE_BASE

  可以看出他是取線性地址的最高10位,再左移2位,加上頁目錄的基址,得到該線性地址在該程序的 頁目錄的索引,而該處存放的就是其對應的頁表索引,第二句:

  PdePageNumber = MI_GET_PAGE_FRAME_FROM_PTE PointerPte;

  MI_GET_PAGE_FRAME_FROM_PTE 這個巨集mi386.h 2542行的定義如下:

  #define MI_GET_PAGE_FRAME_FROM_PTEPTE PTE->u.Hard.PageFrameNumber

  他是直接取該頁表對應的實體記憶體的地址。到這裡,CPU就可以訪問到實際的實體記憶體了。

  剩下的工作就是把cr3暫存器的內容賦給dirbase,再把dirbase賦給程序pcb的DirectoryTableBase項,這個在windbg除錯的時候我說過了。後面再把該程序的低2G的使用者空間清零。

  我舉個例子,咱來一步步轉換。我關閉了PAE,關閉方法,網上找,xp sp3

  假如線性地址為7c920000,這個地址一般載入的是程序的ntdll檔案,首先把它轉換成二進位制,我在windbg裡做,如下:

  kd> .formats 7c920000

  Evaluate expression:

  Hex: 7c920000

  Decimal: 2089943040

  Octal: 17444400000

  Binary: 01111100 10010010 00000000 00000000

  Chars: |...

  Time: Mon Mar 24 11:44:00 2036

  Float: low 6.0646e+036 high 0

  Double: 1.03257e-314

  可以看到高10位為01111100 10十六進位制為0x1f2,

  01111100 10 ->0x1F2按照上面的巨集,0x1F2在左移2位,為0x7C8,加上PDE的基址C0300000,結果為C03007C8,我們在windbg裡看下,如下:

  kd> !pte 7c920000

  VA 7c920000

  PDE at C03007C8 PTE at C01F2480

  contains 03793067 contains 03791025

  pfn 3793 ---DA--UWEV pfn 3791 ----A--UREV

  可以看到,結果是吻合的,下面我們來算頁表,按照上面的說法,一個程序的頁目錄可以達到1024項2的10次方,而每項都是4位元組,所以一個程序的頁目錄大小為4096bytes4k,同理,一個頁表的大小也為4096bytes4k,而每一個頁目錄項對應一個4k大小的頁表,所以,頁表項的地址可以這麼算:

  PTE = 頁表基址+頁目錄項索引 × 一個頁表的大小 + 頁表索引 × 每一個頁表項的大小

  由上面的巨集定義知道在關了PAE的情況下頁表的基址PTE_BASE為0xc0000000,一個頁表項的大小也為4位元組。而該線性地址的中間10位為010010 0000十六進位制為0x120,所以,該線性地址對應的頁表地址為:

  PTE = 0xC0000000 + 0x1F2 * 0x1000 + 0x120 * 0x4 = 0xC01F2480.和上面windbg顯示的資料是一樣的。

  在windbg裡看下該處的內容為

  kd> dd C01F2480

  c01f2480 03791025 00000000 00000000 04157025

  該頁表處的內容為0x03791025 ,其中,如果瞭解頁表結構的話,其高20位為該頁表指向的實體記憶體,為0x3791,再加上線性地址的末12位偏移,所以該線性地址對應的實際實體地址為:0X37910000.

  到這裡就算完了。

  補充:

  核心定義的頁表的資料結構如下:

  typedef struct _MMPTE {

  union {

  ULONG Long;

  HARDWARE_PTE Flush;

  MMPTE_HARDWARE Hard;

  MMPTE_PROTOTYPE Proto;

  MMPTE_SOFTWARE Soft;

  MMPTE_TRANSITION Trans;

  MMPTE_SUBSECTION Subsect;

  MMPTE_LIST List;

  } u;

  } MMPTE;

  typedef MMPTE *PMMPTE;

  typedef struct _MMPTE_HARDWARE {

  ULONG Valid : 1;

  #if definedNT_UP

  ULONG Write : 1; // UP version

  #else

  ULONG Writable : 1; // changed for MP version

  #endif

  ULONG Owner : 1;

  ULONG WriteThrough : 1;

  ULONG CacheDisable : 1;

  ULONG Accessed : 1;

  ULONG Dirty : 1;

  ULONG LargePage : 1;

  ULONG Global : 1;

  ULONG CopyOnWrite : 1; // software field

  ULONG Prototype : 1; // software field

  #if definedNT_UP

  ULONG reserved : 1; // software field

  #else

  ULONG Write : 1; // software field - MP change

  #endif

  ULONG PageFrameNumber : 20;

  } MMPTE_HARDWARE, *PMMPTE_HARDWARE;

  可以看出上面那個取實體地址的巨集直接取的就是PageFrameNumber ,也就是該地址內容的高20位。

  而在!pte命令在還顯示了一些大些字母,如D,A,W,U之類的,也在該結構中有所顯示。