• 操作系统真象还原_访问vaddr对应的pte


    须知:
    只要开启了分页机制,不管物理地址还是虚拟地址在CPU面前都按照分页处理,也就是即便给出物理地址CPU也按虚拟地址对待。

    为什么没有出现页目录表结构体,也没有页目录项结构体。页目录表在某一块内存中,页表也在某一块内存中。只要用指针指向这些内存块中的某一个地址就能操控页目录表项和页表项,并不需要一个专门的的数据结构来定义页目录表和页表。
    ## 代码 \boot\loader6_3.s ``` ;第二步: ;将页目录表物理地址赋值给cr3寄存器,分页机制打开前要将页表地址加载到控制寄存器cr3中 mov eax, PAGE_DIR_TABLE_POS ; PAGE_DIR_TABLE_POS = 0x100000 mov cr3, eax ```

    \kernel\memory.c

    /** 
     * 功能:
     *      得到参数vaddr所在的pte指针,指针的值也就是虚拟地址。
     *      通过vaddr构造一个新的虚拟地址new_vaddr,该新地址能够访问到vaddr所在pte。
     * 参数:
     *      vaddr   虚拟地址,分为三个部分,0000_0000_00|0000_0000_00|0000_0000_0000
     *                                      pde_num      pte_num      p_add
     * 说明:
     *      处理器第一次当成页目录表处理,第二次当成页表处理,第三次当成普通页处理
     * 
     *      分三步:
     *          第一步,CPU将页目录表当成页目录表
     *          第二步,CPU将页目录表当成页表
     *          第三步,CPU将页目录表当成页(也就是页帧)
     */
    uint31_t *pte_ptr(uint32_t vaddr)
    {
        // 0xffc00000 = 1111 1111 1100 0000 0000 0000 0000 0000 
        // 1111_1111_11b=0x3ff=1023 
        uint32_t *pte = (uint32_t *)(0xffc00000  // new_vaddr的页目录项下标,只是为了获得页目录表的物理地址
                        + ((vaddr & 0xffc00000) >> 10)  // 真正的页目录项下标,在cpu眼里是页表项下标
                        + PTE_IDX(vaddr) * 4);  // 真正页表项下标,在cpu眼里是普通页中的地址
        
        // 得到一个新的地址,新地址高10位必定是0xffc;中间10位是vaddr的高10位;低12位中的高10位是vaddr的中间10位。
        return pte;
    }
    
    /** 
     * 功能:
     *      根据vaddr来构造一个新的32位地址new_vaddr。
     * 参数:
     *      vaddr                   虚拟地址                
     * 说明:
     *      new_vaddr为虚拟地址vaddr对应的pde的指针得到虚拟地址vaddr所在pde的指针,也就
     *      是返回能够访问该pde的虚拟地址。
     */
    uint32_t *pde_ptr(uint32_t vaddr)
    {
        /* 0xfffff是用来访问到页表本身所在的地址,如果new_vaddr的低12位为0,则访问到的是页表的起始虚拟地址 */
        uint32_t *pde = (uint32_t *)((0xfffff000) + PDE_IDX(vaddr) * 4);
        return pde;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    MMU机制

    页目录表物理地址已经存放在cr3寄存器,只要开启了分页机制,任何地址在CPU眼里都要cr3+pde_num*4找到页目录项,从页目录项中取出页表的地址;再用页表地址+pte_num*4找到页表项,再从页表项中找到页框地址;再用p_add在该页框中定位具体地址。

    正常虚拟地址访问

    假如vaddr = 0x7ffdac7f = (0111 1111 11)(11 1101 1010) 1100 0111 1111
                 pde_num   pte_num   p_add

    ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=vim_md_image%2F%E6%99%AE%E9%80%9A%E8%99%9A%E6%8B%9F%E5%9C%B0%E5%9D%80%E6%98%A0%E5%B0%84.png&pos_id=img-cap8xNr1e5c702f.png5

    • 第一步:
      • 1:MMU从cr3中取出页目录表的物理地址。
      • 2:MMU用cr3中的页目录表的物理地址+511*4定位到页目录项的物理地址。
    • 第二步:
      • 1:MMU从下标为511的页目录项中取出页表的物理地址。
      • 2:MMU用页表的物理地址+986*4,定位到对应的页表项的物理地址。
    • 第三步:
      • 1:MMU从下标为986的页表项中取出普通页框的物理地址。
      • 2:MMU用普通页框的物理地址+ 3199,从而定位到了真正的物理地址。

    为什么要构造虚拟地址vaddr对应的pte指针

    虚拟地址vaddr从虚拟地址池分配出来后,是无法访问的,因为没有建立物理地址映射。为此需要在页目录表中建立所在的页目录项和页表项。

    虚拟地址范围与页目录项是一一对应的关系的,同样与页表项也一一对应,这种对应关系是固定的。4G范围的虚拟地址空间对应1024个页目录项,同样4M范围的虚拟地址对应1024个页表项。

    虚拟地址空间与页表项、页目录项对于关系

    请添加图片描述

    虚拟地址vaddr = 0x7ffdac7 f对应页目录项下标为511,对应的页表项下标为986。也就是访问vaddr必须通过下标为511页目录项和下标为986页表项中转。因此,要将下标为986的页表项所在页表的物理地址存入下标为511页目录项中,要将普通页框的物理地址下标为986页表项中。

    这就是函数pde_ptr()和pte_ptr()存在的意义。利用vaddr与页目录项和页表项一一对应的关系,计算出要操作哪个页目录项和页表项,从而改写目录项与页表项中的内容,建立物理地址与虚拟地址的映射关系。

    构造虚拟地址vaddr对应的pte指针

    要构造一个新的虚拟地址new_vaddr,该新地址要能够访问到vaddr所在pte。也就是得到虚拟地址 vaddr 对应的 pte 指针。

    将vaddr作为参数,调用pte_ptr()得到:

    *pte = (uint32_t *)(0xffc00000 + ((0x7ffdac7f & 0xffc00000) >> 10) 
                        + ((0x7ffdac7f & 0x003ff000) >> 12) * 4); 
    
    • 1
    • 2

    0x7ffdac7f = 0111 1111 1111 1101 1010 1100 0111 1111
    0xffc00000 = 1111 1111 1100 0000 0000 0000 0000 0000

    (0x7ffdac7f & 0xffc00000) = 0x7fc00000 = 0111 1111 1100 0000 0000 0000 0000 0000
    = (uint32_t *)(0xffc00000 + (0x7fc00000 >> 10) + ((0x7ffdac7f & 0x003ff000) >> 12) * 4)

    0x7fc00000 >> 10 = 0111 1111 1100 0000 0000 00 = 0x1ff000
    = (uint32_t *)(0xffc00000 + 0x1ff000 + ((0x7ffdac7f & 0x003ff000) >> 12) * 4)

    (0x7ffdac7f & 0x003ff000) = 0x3DA000 = 0011 1101 1010 0000 0000 0000
    = (uint32_t *)(0xffc00000 + 0x1ff000 + (0x3DA000 >> 12) * 4)

    0x3DA000 >> 12 = 0011 1101 1010 00 = 0x3da
    0x3da * 4 = 0xf68

    = (uint32_t *)(0xffc00000 + 0x1ff000 + 0xf68)

    0xffc00000 = 1111 1111 1100 0000 0000 0000 0000 0000
    0x1ff000 = 0000 0000 0001 1111 1111 0000 0000 0000
    0xf68 = 0000 0000 0000 0000 0000 1111 0110 1000

    0xffc00000 + 0x1ff000 + 0xf68 = 0xFFDFFF68
    = (1111 1111 11)(01 1111 1111) (1111 0110 1000)
    pde_num    pte_num    p_add

    = (uint32_t *)(0xFFDFFF68)

    因此得到:

    • pde_num = 0x3ff
    • pte_num = 0x1ff
    • p_add = 0xf68

    访问vaddr对应的pte

    由于页目录表中的最后一个页目录项(也就是下标为1023的页目录项)中存储的是页目录表的物理地址,所以只要给出的虚拟地址new_vaddr的高十位是0x3ff,那么,原本new_vaddr的中间10位访问的是别的页表中的页表项就不会发生,相反,new_vaddr的中间10位(原本作为页表项的下标)仍然访问的页目录项的物理地址。new_vaddr的低12位访问的才是别的页表的页表项。

    • 第一步:
      • 1:MMU从cr3中取出页目录表的物理地址。
      • 2:MMU用cr3中的页目录表的物理地址+1023*4定位到页目录项的物理地址。
    • 第二步:
      • 1:MMU从下标为1023的页目录项中再次取出页目录表的物理地址。
      • 2:MMU用下标为1023的页目录项中存储的页目录表的物理地址+511*4,再次定位到对应页目录项的物理地址。
    • 第三步:
      • 1:MMU从下标为511的页目录项中取出页表的物理地址。
      • 2:MMU用下标为511的页目录项中存储的页表的物理地址+3944,从而定位到vaddr对应的pte的物理地址。

    总结

    当正常访问vaddr时,页目录表访问一次,页表访问一次,普通页框访问一次。
    当访问vaddr对应的pte时,页目录表访问次,页表访问一次。

  • 相关阅读:
    迅为IMX8M开发板设备树下的platform驱动实验程序编写
    揭秘小程序上线不到一周,每天2万销售额,究竟怎么做到的?
    Web安全总结
    ERP对接淘宝/天猫/京东/拼多多商品详情数据API接口
    学习记忆——宫殿篇——记忆宫殿——数字编码——扑克牌记忆
    【Cherno的OpenGL视频】Creating tests in OpenGL
    【游戏客户端】制作节奏大师Like音游(下)
    NPDP产品经理知识(产品设计与开发工具)
    自由无拘束 动听够震撼 飞利浦GO系列骨传导耳机A7607追光上市
    流程图高级用法【Markdown进阶篇】
  • 原文地址:https://blog.csdn.net/yangjia_cheng/article/details/133131985