• 1248阅读
  • 1回复

线性地址到物理地址的转换 [复制链接]

上一主题 下一主题
离线啊冲
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-03
我详细说下虚拟地址的转换。

首先,来看下内核为一个进程建立页目录和页表以及线性空间的函数。这个函数原型是:
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 MiGetPdeAddress(va)  ((PMMPTE)(((((ULONG)(va)) >> 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_PTE(PTE) ((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字节,所以一个进程的页目录大小为4096bytes(4k),同理,一个页表的大小也为4096bytes(4k),而每一个页目录项对应一个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 defined(NT_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 defined(NT_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之类的,也在该结构中有所显示。
善者 慈悲心常在 无怨无恨 以苦为乐
默认压缩密码www.hifyl.com
文件分享密码问题:http://www.hifyl.com/read-htm-tid-4444.html
离线v2680267313

只看该作者 沙发  发表于: 2016-04-30
用户被禁言,该主题自动屏蔽!
快速回复
限100 字节
批量上传需要先选择文件,再选择上传
 
上一个 下一个