• Windows保护模式(八)TLB&控制寄存器


    TLB

    TLB(Translation Lookaside Buffer)是 CPU 内部用于缓存线性地址与物理地址映射关系的表。

    TLB结构

    在这里插入图片描述

    • ATTR:属性
      • 在10-10-12分页模式下:ATTR = PDE属性 & PTE属性
      • 在2-9-9-12分页模式下:ATTR = PDPTE属性 & PDE属性 & PTE属性
    • LRU:统计信息
      由于TLB的大小有限,因此当TLB被写满、又有新的地址即将写入时,TLB就会根据统计信息来判断哪些地址是不常用的,从而将不常用的记录从TLB中移除。

    注意:

    • 不同的CPU,TLB大小不同
    • 只要Cr3发生变化,TLB立即刷新,一核一套TLB
    • 由于操作系统的高2G映射基本不变,因此如果Cr3改了,TLB刷新的话,重建高2G以上很浪费。所以PDE和PTE中有个G标志位(当PDE为大页时,G标志位才起作用),如果G位为1,刷新TLB时将不会刷新PDE/PTEG位为1的页,当TLB写满时,CPU根据统计信息将不常用的地址废弃,保留最常用的地址

    TLB种类

    TLB在X86体系的CPU中的实际应用最早是从Intel的486CPU开始的,在X86体系的CPU中,一般都设有如下4组TLB:

    • 第一组:缓存一般页表(4K字节页面)的指令页表缓存(Instruction-TLB);
    • 第二组:缓存一般页表(4K字节页面)的数据页表缓存(Data-TLB);
    • 第三组:缓存大尺寸页表(2M/4M字节页面)的指令页表缓存(Instruction-TLB);
    • 第四组:缓存大尺寸页表(2M/4M字节页面)的数据页表缓存(Data-TLB)

    实验

    体验TLB的存在

    实验在 2-9-9-12 分页的环境下进行。代码如下:

    #include 
    #include 
    #include 
    
    void *p1, *p2;
    
    unsigned int val1, val2, val3;
    unsigned int plt1, plt2;
    const unsigned int plt0 = 0xC0000000;
    
    __declspec(naked) void callGate() {
        __asm {
            push 0x30
            pop fs
            pushad
            pushfd
        }
        //将 p1 对应物理页挂到 0 地址处
        plt1 = ((unsigned int) p1 >> 9 & 0x7FFFF8) | 0xC0000000;
        *(unsigned int *) plt0 = *(unsigned int *) plt1;
        *(unsigned int *) (plt0 + 4) = *(unsigned int *) (plt1 + 4);
        //读取 0 地址处内存
        val1 = *(unsigned int *) 0;
        //将 p2 对应物理页挂到 0 地址处
        plt2 = ((unsigned int) p2 >> 9 & 0x7FFFF8) | 0xC0000000;
        *(unsigned int *) plt0 = *(unsigned int *) plt2;
        *(unsigned int *) (plt0 + 4) = *(unsigned int *) (plt2 + 4);
        //读取 0 地址处内存
        val2 = *(unsigned int *) 0;
        //刷新 TLB
        __asm {
            mov eax, cr3
            mov cr3, eax
        }
        //读取 0 地址处内存
        val3 = *(unsigned int *) 0;
    
        __asm {
            popad
            popfd
            retf
        }
    }
    
    
    int main() {
        unsigned char buf[] = {0, 0, 0, 0, 0x48, 0};
        p1 = VirtualAlloc((void *) 0x600000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        p2 = VirtualAlloc((void *) 0x700000, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        printf("%X %X\n", (int) p1, (int) p2);
        if (p1 == NULL || p2 == NULL) {
            printf("[-] VirtualAlloc failed.");
            return 0;
        }
        *(unsigned int *) p1 = 0x123;
        *(unsigned int *) p2 = 0x456;
        printf("callGate: %X\n", callGate);
        system("pause");
        __asm {
            pushad
            pushfd
            call fword ptr buf
            popfd
            popad
            push 0x3b
            pop fs
        }
        printf("val1: %X\nval2: %X\nval3: %X\n", val1, val2, val3);
        return 0;
    }
    
    • 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
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    运行结果:
    在这里插入图片描述

    可以发现:

    • 在 val1 被赋值完成后,即使 0 地址被挂上了新的物理页,再对 val2 进行赋值,val1 和 val2 输出的值是相同的。
    • 在 Cr3 刷新后,0地址没有被挂上新的物理页,对 val3 进行赋值后,val3 却输出了新的值。

    这是因为 Cr3 刷新前,0 地址第一次被val1访问时,线性地址与物理地址的对应关系被写入了 TLB 中,因此在对 val2 赋值时,TLB 的记录没有被刷新,访问的还是原来的物理页。

    体验全局页的意义

    将前面的代码做如下改动:
    在这里插入图片描述
    由于 p1 的 PLT 写入 0 地址对应 PLT 时将 G 位 置 1,刷新 TLB 时不会更新该数据。

    INVLPG指令的意义

    在上一实验代码的基础上使用 INVLPG 指令刷新 0 地址对于的 TLB 数据,结果在 PLT 的 G 位置 1 的情况下依然能刷新。
    在这里插入图片描述

    控制寄存器

    描述

    控制寄存器用于控制和确定CPU的操作模式。控制寄存器有五个,分别是:Cr0 Cr1 Cr2 Cr3 Cr4。其中Cr1保留,Cr3为页目录表基址。

    Cr0寄存器

    结构图:
    在这里插入图片描述

    • PE位:启用保护(Protecction Enable)标志

      • PE=1:保护模式
      • PE=0:实地址模式

      这个标志仅开启段级保护,而没有启用分页机制
      若要启用分页机制,那么PE和PG标志都要置位

    • PG位:分页机制标志

      • PG=1:开启了分页机制
      • PG=0:未开启分页机制

      在开启这个标志位之前必须已经或者同时开启PE标志

      • PG=0且PE=0:处理器工作状态为实地址模式
      • PG=0且PE=1:处理器工作状态为没有开启分页机制的保护模式
      • PG=1且PE=0:不存在。在PE没有开启的情况下无法开启PG
      • PG=1且PE=1:处理器工作状态为开启了分页机制的保护模式
    • WP位:写保护(Write Proctect)标志
      对于Intel 80486或以上的CPU,CR0的16位是写保护标志
      当设置该标志时,处理器会禁止超级用户程序(例如特权级0的程序)向用户级只读页面执行写操作

      当CPL<3的时候:

      • 如果 WP=0 可以读写任意用户级物理页,只要线性地址有效
      • 如果 WP=1 可以读取任意用户级物理页,但对于只读的物理页,则不能写

    Cr2寄存器

    描述:
    当CPU访问某个无效页面时,会产生缺页异常,此时,CPU会将引起异常的线性地址存放在CR2中。
    结构图:
    在这里插入图片描述
    当CPU访问某个无效页面时,会产生缺页异常,此时,CPU会将引起异常的线性地址存放在CR2中。

    Cr4寄存器

    结构图:
    在这里插入图片描述

    • PAE:
      • PAE=1:2-9-9-12分页
      • PAE=0:10-10-12分页
    • PSE:大页(PS=1)是否有效
  • 相关阅读:
    Java9-17新特性一览,了解少于3个你可能脱节了
    Docker环境搭建Prometheus实验环境
    【LeetCode刷题】146. LRU 缓存
    这才是你需要的 C 语言、C++ 学习路线!
    微信小程序简单输入框界面(实现选择标签功能)
    matlab矩阵的输入
    Python编程经典案例【考题】排列组合
    [MATLAB]进阶绘图
    作为产品经理,你是如何分析和管理你的产品需求的?
    OpenJudge NOI题库 1.6 编程基础之一维数组
  • 原文地址:https://blog.csdn.net/qq_45323960/article/details/127416668