• 内核01-保护模式-分页机制


    概述

    每个进程都有0~4Gb的虚拟地址空间, 但是每个进程所使用的虚拟地址空间映射到的物理内存都是不一样的.

    意思就说是, 同样是虚拟地址0x401000 , 在不同的进程中能够访问出不同的数据来.

    这是因为Windows使用CPU的分页机制, 将一个进程的虚拟地址映射到了不同的物理地址上.

    1566884644917

    分页机制

    当需要使用一个虚拟地址时, 就需要通过WindowsAPI先预定内存,然后再提交内存, 这样之后才能使用.

    而分配提交的过程实际是这样的:

    Windows把需要提交的虚拟地址(例如:0x403000)和对应的物理地址(例如是0x10000) 建立一个映射关系.

    从此以后, 当使用虚拟地址0x403000的时候, CPU会直接修改0x10000的物理内存.

    这个虚拟地址和物理地址的映射关系需要记录下来. 在Windows中,有几套记录的方式, 下面是其中的一套方式:

    • 非物理地址扩展模式

    • 物理地址扩展模式(PAE)

    下面说明非物理地址扩展模式

    非物理地址扩展模式

    在这个模式下, Windows使用使用一写表来记录物理地址和虚拟地址的对应关系, 简单来说, 它们可以是这样子的:

    1566884695023

    例如, 当要使用一个虚拟地址时, 可以先找到物理内存中没有被使用过的地址, (怎么才知道一个物理地址没有被使用过? 找一下保存物理地址的这张表看看有没有记录就行了,有记录说明已经有虚拟地址和这个物理地址映射了,不能重新映射). 虚拟地址和物理地址建立映射时,直接把虚拟地址和物理地址记录到表中就可以了.

    但是这样一来, 这张表也是需要保存在内存中的, 一个地址需要用到4字节的内存空间保存 , 虚拟地址总共有4Gb个, 那总共需要多少字节来保存这些地址? 需要 4字节 * 4Gb把? 但我们的物理内存没有这么多啊.

    因此, 虚拟地址和物理地址的映射关系不能这么保存.

    Windows想到了一个绝妙的方法:

    1. 把物理内存中的按照4096字节一个单位管理, 每4096称为一个分页(4Kb). 这样一来, 4Gb的内存空间有1048576个分页, 也就是说, 保存这1048576个分页首地址,总共需要的内存空间为: 1048576*4(每个地址需要4字节) = 4194304字节.这是多少大? 这是4M的空间. 这4M那么大的空间中, 每4个字节都是一个页表的首地址, 而这4M的空间被称为页表目录.

    2. Windows又将页表目录再分成每4096个页表目录作为一组, 形成一个数组, 这个数组称为页目录表, 其形式如下图:

      1566884850262

    当需要使用一个虚拟地址时, Windows就会将这个虚拟基地址记录在上面的结构中, 如何记录?

    例如地址: 403001. 这个地址会被拆分成3个部分

    • 页目录表索引(10个比特位)

    • 页表索引(10个比特位)

    • 字节索引(12个比特位)

    例如, 虚拟地址:0x00403001, 将这个地址的二进制分成如下形式:

    00000000 01000000 00110000 00000001

    然后拆分成10比10比12的形式:

    0000000001 0000000011 000000000001

    然后将三组二进制对应的数值求出: 1 : 3 : 1

    这1 : 3 : 1 分别就是--页目录表索引 : 页表索引 : 字节索引.

    这里的索引, 就是下标的意思, 说白了, 这三个数中的前两个数就是下标(第一个1就是页目录数组中的下标为1的元素, 第二个3就是页表数组中下标为3的元素, 第三个1表示在页表内的偏移.)

    这个过程可以在windbg中查看到:

    注意,在做以下试验时, 必须关闭系统的PAE模式,关闭方法为:

    1. 确保虚拟机是win7 32位系统, 且CPU个数是1个,内存大小为4Gb以下.

    2. 以管理员方式运行CMD , 输入以下指令:

      BCDEdit /set PAE ForceDisable
      BCDEdit /set NX AlwaysOff
    3. 重启系统

    例如,下面有程序,代码为:

    int main()
    {
       printf("%p" , "aaabbbccc");
    }

    将程序编译,改名为test.exe并在虚拟机中运行后, 程序将输出字符串的地址:0x417B30, 这个地址就是一个虚拟地址,下面将这个地址转换为物理地址:

    1566884931728

    1. 通过windbg的!process命令查看进程信息,获取进程的页目录首地址:

      # !process命令的格式为: !process 0 0 进程名
      kd> !process 0 0 test.exe
      PROCESS 87c46030 SessionId: 1 Cid: 08ac   Peb: 7ffdf000 ParentCid: 05ec
         DirBase: 3946f000 ObjectTable: ac410490 HandleCount:   9.
           Image: test.exe

      在输出的信息中, DirBase:3946f000就是本进程的页目录首地址了, 注意, 每个进程的页目录首地址都是不一样的.每个进程都有自己的页目录. 需要注意的是, 这个页目录地址是一个物理地址, windbg中的d系列命令只能查看虚拟地址, 不能查看物理地址, 查看物理地址需要使用!d系列命令.

    2. 查看页目录的数据

      kd> !dd 3946f000 
      #3946f000 387f0867 392ec867 00000000 00000000
      #3946f010 00000000 00000000 00000000 00000000
      #3946f020 00000000 00000000 00000000 00000000

      查看之后, 命令输出了很多地址, 这些地址都是物理地址. 实际上就是页表的首地址.

      这些首地址的后3个十六进制数是属性, 只有高30位才是有效地址,后12位是属性. 属性如图所示:

      1566885056391

    3. 将虚拟地址0x41B730的二进制展开

      kd> .formats 41b730
      Evaluate expression:
      Hex:     0041b730
      Decimal: 4306736
         Octal:   00020333460
         Binary: 00000000 01000001 10110111 00110000
         Chars:   .A.0
           Time:   Fri Feb 20 04:18:56 1970
           Float:   low 6.03502e-039 high 0
             Double: 2.12781e-317

      得到00000000 01000001 10110111 00110000, 按照10-10-12的形式展开:

      0000000001 0000010111 101100110000求出的值为: 1-17-B30.

      其中1表示页目录中的第1项, 也就是0x392ec867 , 这个地址就是页表首地址和页表的属性结合的值.

      这个地址的低12位是属性, 高24位才是页表的物理地址,也就是0x392ec000;

      1566885162565

    4. 查看页表数据

      kd> !dd 392ec000 + 17 *4
      #392ec05c 38d6d025 38b2e025 00000000 37993867
      #392ec06c 3780b025 38dfc225 3761c025 3a0c1025
      #392ec07c 00000000 00000000 00000000 00000000

      上述数据就是页表中的数据, 页表内的数据每个元素都是一个内存分页的首地址. 在这个数组中挑选一个出来,要挑选出来的下标为17(虚拟地址被拆分的值为:1-17-B30).

      也就是说, 392ec00 + 17 * 4就是要找的分页了.

      因此在上面的命令中, 392ec00是首地址, 17是下标, *4是因为数组元素是4字节一个.

      得到的内存页的值为 : 38d6d025, 去掉后12个二进制位得到的值是:38d6d000

    5. 查看页内偏移

      kd> !db 38d6d000 + B30
      #38d6db30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
      #38d6db40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....

      最后得到的就是物理内存中的数据了.

      这与直接查看虚拟地址得到的结果一致

      kd> db 417b30
      00417b30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
      00417b40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....
    6. 上述过程如果所示

      1566885271223

    完成的windbg命令:

    kd> !process 0 0 test.exe  /* 1. 查看进程信息*/
    PROCESS 87c46030 SessionId: 1 Cid: 08ac   Peb: 7ffdf000 ParentCid: 05ec
       DirBase: 3946f000 /*页目录首地址*/ ObjectTable: ac410490 HandleCount:   9.
       Image: test.exe

    kd> .formats 41b730 /*查看虚拟地址的二进制形式*/
    Evaluate expression:
    Hex:     0041b730
    Decimal: 4306736
    Octal:   00020333460
    Binary: 00000000 01000001 10110111 00110000 /*转换为10-10-12格式==> 1-17-B30*/
    Chars:   .A.0
    Time:   Fri Feb 20 04:18:56 1970
    Float:   low 6.03502e-039 high 0
    Double: 2.12781e-317

    kd> !dd 3946f000 /*查看页目录, 页目录的每个元素是页表首地址,使用虚拟地址中记录的第1项:392ec867 */
    #3946f000 387f0867 392ec867 00000000 00000000
    #3946f010 00000000 00000000 00000000 00000000
    #3946f020 00000000 00000000 00000000 00000000
    #3946f030 00000000 00000000 00000000 00000000
    #3946f040 00000000 00000000 00000000 00000000
    #3946f050 00000000 00000000 00000000 00000000
    #3946f060 00000000 00000000 00000000 00000000
    #3946f070 00000000 00000000 00000000 00000000

    kd> !dd 392ec000 + 17 *4 /*查看页表, 页表中保存的每个元素是分页首地址, 使用虚拟地址中记录的第17项分页:38d6d025*/
    #392ec05c 38d6d025 38b2e025 00000000 37993867
    #392ec06c 3780b025 38dfc225 3761c025 3a0c1025
    #392ec07c 00000000 00000000 00000000 00000000
    #392ec08c 00000000 00000000 00000000 00000000
    #392ec09c 00000000 00000000 00000000 00000000
    #392ec0ac 00000000 00000000 00000000 00000000
    #392ec0bc 00000000 00000000 00000000 00000000
    #392ec0cc 00000000 00000000 00000000 00000000
    kd> !db 38d6d000 + B30 /*分页首地址+页内偏移得到真正的物理地址*/
    #38d6db30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
    #38d6db40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....
    #38d6db50 00 00 00 00 25 70 0a 00-5c 00 3f 00 3f 00 5c 00 ....%p...?.?..
    #38d6db60 43 00 3a 00 5c 00 31 00-2e 00 74 00 78 00 74 00 C.:..1...t.x.t.
    #38d6db70 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
    #38d6db80 70 61 75 73 65 00 00 00-bb f1 c8 a1 ce c4 bc fe pause...........
    #38d6db90 b4 ed ce f3 3a 25 30 38-58 0a 00 00 00 00 00 00 ....:%08X.......
    #38d6dba0 c7 fd b6 af b4 f2 bf aa-b3 c9 b9 a6 0a 00 00 00 ................

    kd> db 417b30 /*直接查看虚拟地址*/
    00417b30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
    00417b40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....
    00417b50 00 00 00 00 25 70 0a 00-5c 00 3f 00 3f 00 5c 00 ....%p...?.?..
    00417b60 43 00 3a 00 5c 00 31 00-2e 00 74 00 78 00 74 00 C.:..1...t.x.t.
    00417b70 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
    00417b80 70 61 75 73 65 00 00 00-bb f1 c8 a1 ce c4 bc fe pause...........
    00417b90 b4 ed ce f3 3a 25 30 38-58 0a 00 00 00 00 00 00 ....:%08X.......
    00417ba0 c7 fd b6 af b4 f2 bf aa-b3 c9 b9 a6 0a 00 00 00 ................

    kd> .process /i 87c46030 /*切换到进程地址空间, 格式为 .process /i PROCESS地址*/
    You need to continue execution (press 'g' <enter>) for the context
    to be switched. When the debugger breaks in again, you will be in
    the new process context.
    kd> g   /*g一下再执行!vtop指令*/
    Break instruction exception - code 80000003 (first chance)
    nt!RtlpBreakWithStatusInstruction:
    8405dd00 cc             int     3
    kd> !vtop 0 417b30 /*查看进程虚拟地址对应的物理地址, 格式为 !vtop 0 虚拟地址*/
    X86VtoP: Virt 00417b30, pagedir 3946f000
    X86VtoP: PDE 3946f004 - 392ec867
    X86VtoP: PTE 392ec05c - 38d6d025
    X86VtoP: Mapped phys 38d6db30
    Virtual address 417b30 translates to physical address 38d6db30.

    物理地址扩展模式(PAE模式)

    PAE模式被称为物理地址扩展模式, 它能使32位的操作系统能够访问超出4Gb的物理内存(非PAE模式下只能访问4Gb以内的内存, 在非PAE模式下, 即使电脑安装的8Gb内存条也只能使用4Gb)

    开始PAE模式的方法为(win7 32位系统默认开启PAE模式):

    1. 确保虚拟机是win7 32位系统, 且CPU个数是1个,内存大小不限.

    2. 以管理员方式运行CMD , 输入以下指令:

      BCDEdit /set PAE Forceenable
      BCDEdit /set NX Alwayson
    3. 重启系统

    开启PAE模式之后, Windows使用另一种模式来管理虚拟地址.

    一个虚拟地址被拆分为4个部分:

    1. 页目录表指针索引(2个二进制位)

    2. 页目录(9个二进制位)

    3. 页表(9个二进制位)

    4. 页内偏移(12个二进制位)

    也就是2-9-9-12

    保存虚拟地址和物理地址映射关系如图:

    1566885318374

    其地址转译过程和非PAE模式下的地址转译过程差不多, 下面是对同一程序的转译过程, 但是需要注意的是, 非PAE模式下使用4个字节保存页表首地址和分页首地址,在PAE模式下, 使用8字节来保存

    程序运行输出的地址仍然还是: 0x417B30

    1566885327112

    kd> !process 0 0 test.exe  /*查看进程信息*/
    PROCESS 87932030 /*切换进程时用到的PROCESS地址*/ SessionId: 1 Cid: 0864   Peb: 7ffda000 ParentCid: 0548
       DirBase: 3e807440/*页目录指针表地址*/ ObjectTable: 8b4e6ef8 HandleCount:   7.
       Image: test.exe

    kd> .formats 417b30 /*查看虚拟地址的二进制形式*/
    Evaluate expression:
    Hex:     00417b30
    Decimal: 4291376
    Octal:   00020275460
    Binary: 00000000 01000001 01111011 00110000 /*拆分成2-9-9-12格式为: 0-2-17-B30*/
    Chars:   .A{0
    Time:   Fri Feb 20 00:02:56 1970
    Float:   low 6.0135e-039 high 0
    Double: 2.12022e-317
    kd> !dq 3e807440   /*查看页目录指针表 ,此表保存的是页目录的首地址,同样低12位是属性.使用第0个即:00000000`37a65801*/
    #3e807440 00000000`37a65801 00000000`38326801
    #3e807450 00000000`38067801 00000000`38128801
    #3e807460 00000000`36ec8801 00000000`36e09801
    #3e807470 00000000`3704a801 00000000`36b8b801
    #3e807480 00000000`355c5801 00000000`35306801
    #3e807490 00000000`35507801 00000000`35488801
    #3e8074a0 00000000`34c90801 00000000`354d1801
    #3e8074b0 00000000`353d2801 00000000`35bd3801

    kd> !dq 00000000`37a65000+2*8 /*查看页目录, 此表保存的是页表首地址, 同样低12位是属性,使用第2个,注意,这里乘的是8,因为每个地址使用8字节来保存了.*/
    #37a65010 00000000`37ef0847 00000000`00000000
    #37a65020 00000000`00000000 00000000`00000000
    #37a65030 00000000`00000000 00000000`00000000
    #37a65040 00000000`00000000 00000000`00000000
    #37a65050 00000000`00000000 00000000`00000000
    #37a65060 00000000`00000000 00000000`00000000
    #37a65070 00000000`00000000 00000000`00000000
    #37a65080 00000000`00000000 00000000`00000000
    kd> !dq 00000000`37ef0000+17*8 /*查看页表, 此表保存的是分页首地址, 同样低12位是属性,使用第17个:80000000`37cf1005 */
    #37ef00b8 80000000`37cf1005 80000000`37cb2005
    #37ef00c8 00000000`00000000 80000000`3701b847
    #37ef00d8 80000000`37513005 80000000`36dc4205
    #37ef00e8 80000000`37e64005 80000000`13563005
    #37ef00f8 00000000`00000000 00000000`00000000
    #37ef0108 00000000`00000000 00000000`00000000
    #37ef0118 00000000`00000000 00000000`00000000
    #37ef0128 00000000`00000000 00000000`00000000
    kd> !db 80000000`37cf1000+B30 /*查看页内偏移*/
    #8000000037cf1b30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
    #8000000037cf1b40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....
    #8000000037cf1b50 00 00 00 00 25 70 0a 00-5c 00 3f 00 3f 00 5c 00 ....%p...?.?..
    #8000000037cf1b60 43 00 3a 00 5c 00 31 00-2e 00 74 00 78 00 74 00 C.:..1...t.x.t.
    #8000000037cf1b70 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
    #8000000037cf1b80 70 61 75 73 65 00 00 00-bb f1 c8 a1 ce c4 bc fe pause...........
    #8000000037cf1b90 b4 ed ce f3 3a 25 30 38-58 0a 00 00 00 00 00 00 ....:%08X.......
    #8000000037cf1ba0 c7 fd b6 af b4 f2 bf aa-b3 c9 b9 a6 0a 00 00 00 ................

    kd> db 417b30
    00417b30 61 61 61 62 62 62 63 63-63 00 00 00 c7 fd b6 af aaabbbccc.......
    00417b40 b4 f2 bf aa ca a7 b0 dc-3a 25 64 0a 00 00 00 00 ........:%d.....
    00417b50 00 00 00 00 25 70 0a 00-5c 00 3f 00 3f 00 5c 00 ....%p...?.?..
    00417b60 43 00 3a 00 5c 00 31 00-2e 00 74 00 78 00 74 00 C.:..1...t.x.t.
    00417b70 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
    00417b80 70 61 75 73 65 00 00 00-bb f1 c8 a1 ce c4 bc fe pause...........
    00417b90 b4 ed ce f3 3a 25 30 38-58 0a 00 00 00 00 00 00 ....:%08X.......
    00417ba0 c7 fd b6 af b4 f2 bf aa-b3 c9 b9 a6 0a 00 00 00 ................

    kd> !vtop 0 417b30
    X86VtoP: Virt 00417b30, pagedir 3e807440
    X86VtoP: PAE PDPE 3e807440 - 0000000037a65801
    X86VtoP: PAE PDE 37a65010 - 0000000037ef0867
    X86VtoP: PAE PTE 37ef00b8 - 8000000037cf1025
    X86VtoP: PAE Mapped phys 37cf1b30
    Virtual address 417b30 translates to physical address 37cf1b30.
  • 相关阅读:
    第二期冲刺会议3
    第二期站立会议2
    意见汇总及改进方案
    第二期站立会议1
    第一期站立会议7
    第一期站立会议6
    第一期站立会议5
    第一期站立会议4
    第一期站立会议3
    第一期站立会议2
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11418314.html
Copyright © 2020-2023  润新知