zoukankan      html  css  js  c++  java
  • 16_TLB与流水线

    1 前面做的实验起始有缺陷

    访问内存之后,后面执行两句代码后;并不能保证刚才访问的代码还在TLB中;有可能被刷新出去了;

    实验验证缺陷:

    代码 不连续 TLB 被淘汰:

    1570543935670

    2万次中有1次被淘汰;由于访问代码不连续

    代码:

    // 7_TLB_test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //

    #include "pch.h"
    #include<stdio.h>
    #include<stdlib.h>
    #include<Windows.h>

    #define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
    #define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
    DWORD g_out;
    DWORD g_OldPte[2];
    #pragma section("data seg", read, write)
    _declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
    _declspec(allocate("data seg"))DWORD page2[1024]; //41c000
    //0x401000
    void _declspec(naked) IdtEntry()
    {

    // 保存旧的pte ,以用来恢复pte 解决不蓝屏
    g_OldPte[0] = PTE(0x41c000)[0];
    g_OldPte[1] = PTE(0x41c000)[1];

    PTE(0x41c000)[0] = PTE(0x41d000)[0];// 不设置G位
    PTE(0x41c000)[1] = PTE(0x41d000)[1];

    __asm invlpg ds : [0x41c000] // 带有 g 位的刷新;

    // 刷新虚拟地址在TLB中
    __asm
    {
    mov eax, ds: [0x41c000];// 这个时候到快表中了 TLB[0x41c000] 中的值因该是 1;
    }

    // pte 修改回来,但是TLB 中存在所以应该还是 1
    PTE(0x41c000)[0] = g_OldPte[0];
    PTE(0x41c000)[1] = g_OldPte[1];

    __asm
    {
    mov eax,ds:[0x41c000]
    mov g_out,eax
    } // 如果前面访问之后还在快表中,那么这里应该是 [0x41d000] 中的 1;
    // 如果不再快表中了 那么是修改回来的 ,本来的 [0x41c000] 中的 2;

    /*__asm {
    mov eax, cr3
    mov cr3, eax
    }*/
    __asm invlpg ds : [0x41c000] // 带有 g 位的刷新;
    //// 调用调用;确保在 TL B 中
    //__asm mov eax, ds:[0x41c000];

    //// 恢复到原来的pte 
    ////---- 这样
    //// 按道理 后面一旦后面刷新 TLB 将 普通 TLB 刷新出去,
    //// 那么 g_out = page2[0] 的值就 应该是 正常 的原 pte 对应的数据 -- 2。
    //PTE(0x41c000)[0] = g_OldPte[0];
    //PTE(0x41c000)[1] = g_OldPte[1];

    //__asm
    //{
    //mov eax, cr3
    //mov cr3, eax
    //}


    //g_out = page2[0]; // 讲道理 在非G位下 应该是2(原PTE解析出的) -- 但是这里我们设置了PTE 的G位,
    // // so 这里应该是 TLB快表中 对应的 1;


    __asm {
    iretd
    }
    }
    void _declspec(naked) go() {
    {
    pagel[0] = 1; //确保物理页存在
    page2[0] = 2;

    }
    __asm int 0x20
    __asm ret
    }
    //eq 8003f500 0040ee00~ 00081000
    void main()
    {
    if ((DWORD)IdtEntry != 0x401040)
    {
    printf("wrong addr: %p", IdtEntry);
    exit(-1);
    }
    for (int i = 0; i < 200000; i++)
    {
    go();
    if (g_out == 2)
    {
    printf("%d : %d ==== ",i, g_out);
    printf("%d : %d ", i, page2[0]); // 打印出来 还是有小概率 不相同;说明还是在快表中的。
    }
    }

    system("pause");
    }

    G位即使存在 访问代码不连续也可能被淘汰:

    1570543626010

    我这里循环了20000次 ,有1次G位即使在也被TLB淘汰了。

    代码:

    // 7_TLB_test.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //

    #include "pch.h"
    #include<stdio.h>
    #include<stdlib.h>
    #include<Windows.h>

    #define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
    #define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
    DWORD g_out;
    DWORD g_OldPte[2];
    #pragma section("data seg", read, write)
    _declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
    _declspec(allocate("data seg"))DWORD page2[1024]; //41c000
    //0x401000
    void _declspec(naked) IdtEntry()
    {

    // 保存旧的pte ,以用来恢复pte 解决不蓝屏
    g_OldPte[0] = PTE(0x41c000)[0];
    g_OldPte[1] = PTE(0x41c000)[1];

    PTE(0x41c000)[0] = PTE(0x41d000)[0]| 0x100;// 设置G位
    PTE(0x41c000)[1] = PTE(0x41d000)[1];

    __asm invlpg ds : [0x41c000] // 带有 g 位的刷新;

    // 刷新虚拟地址在TLB中
    __asm
    {
    mov eax, ds: [0x41c000];// 这个时候到快表中了 TLB[0x41c000] 中的值因该是 1;
    }

    // pte 修改回来,但是TLB 中存在所以应该还是 1
    PTE(0x41c000)[0] = g_OldPte[0];
    PTE(0x41c000)[1] = g_OldPte[1];

    __asm
    {
    mov eax,ds:[0x41c000]
    mov g_out,eax
    } // 如果前面访问之后还在快表中,那么这里应该是 [0x41d000] 中的 1;
    // 如果不再快表中了 那么是修改回来的 ,本来的 [0x41c000] 中的 2;

    //__asm invlpg ds : [0x41c000] // 带有 g 位的刷新;即前面如果还在快表中,这里刷新pte再返回3环程序后再次输出。做测试用的没有意义了。
    __asm {
    iretd
    }
    }
    void _declspec(naked) go() {
    {
    pagel[0] = 1; //确保物理页存在
    page2[0] = 2;

    }
    __asm int 0x20
    __asm ret
    }
    //eq 8003f500 0040ee00~ 00081000
    void main()
    {
    if ((DWORD)IdtEntry != 0x401040)
    {
    printf("wrong addr: %p", IdtEntry);
    exit(-1);
    }
    for (int i = 0; i < 20000; i++)
    {
    go();
    if (g_out == 2)
    {
    printf("%d : %d ==== ",i, g_out);
    printf("%d : %d ", i, page2[0]); // 打印出来 还是有小概率 不相同;说明还是在快表中的。
    }
    }

    system("pause");
    }

    总结: 注意啊 ::: 切换 cr3 刷新TLB G位的 无影响; 得 使用

    __asm invlpg ds : [0x41c000] // 无视 g 位的刷新;

    3 流水线指令TLB 和数据TLB 得相互影响

    前置知识 : 如果我们的页面没有可执行属性的话;在没有TLB中时,我们修改pte后,第一次访问 绝对时修改pte之后的对应的物理页数据;但是如果有可执行属性,那么在cpu 流水线 技术( 执行指令的时候,也在取指令,且根据将会执行的可能性提前取指令):

    1570545166831

    现象:

    没有 可执行属性的时候;且没有主动加入快表的时候;坑定 是可预计的数据:

    1570545279564

    但是 一旦加入了可执行属性,流水线的预先取指令,可能执行到这儿,取后面的指令:

    1570545354161

    发现后面的指令有 0x405000 ,而且 0x405000 有可执行属性,可能就预先取了这个指令;继而访问;加入了TLB。 (但是即使这样这里也是指令TLB ,但是这里影响到了 数据TLB,但是概率也不高)

    1570549800314

    代码:

    #include "pch.h"
    #include<stdio.h>
    #include<stdlib.h>
    #include<Windows.h>

    #define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
    #define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
    DWORD g_out;
    DWORD g_OldPte[2];
    #pragma section("data seg", read, write)
    _declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
    _declspec(allocate("data seg"))DWORD page2[1024]; //41c000
    //0x401000
    void _declspec(naked) IdtEntry()
    {
    // 保存旧的pte ,以用来恢复pte 解决不蓝屏
    g_OldPte[0] = PTE(0x41c000)[0];
    g_OldPte[1] = PTE(0x41c000)[1];
    __asm{
    mov eax,cr3
    mov cr3,eax
    }
    //__asm mov eax, ds :[0x41b000]
    PTE(0x41c000)[0] = PTE(0x41d000)[0];
    PTE(0x41c000)[1] = PTE(0x41d000)[1];
    __asm {
    mov eax, ds :[0x41c000]
    mov g_out, eax
    }
    PTE(0x41c000)[0] = g_OldPte[0];
    PTE(0x41c000)[1] = g_OldPte[1];
    _asm {
    mov eax, cr3
    mov cr3, eax
    iretd
    }
    }
    void _declspec(naked) go()
    {

    pagel[0] = 0xc3; //
    page2[0] = 0xc390;
    ((void(*)())(DWORD)pagel)();
    ((void(*)())(DWORD)page2)();
    __asm int 0x20
    __asm ret
    }
    //eq 8003f500 0040ee00 00081000
    void main()
    {
    if ((DWORD)IdtEntry != 0x401040)
    {
    printf("wrong addr: %p", IdtEntry);
    exit(-1);
    }

    for (int i = 0; i < 10000; i++) {
    go();
    if (g_out != 0xc3)
    printf("%d: %p ",i, g_out);

    }
    system("pause");
    }


  • 相关阅读:
    非线性方程(组):高维方程解法
    非线性方程(组):一维非线性方程(二)插值迭代方法 [MATLAB]
    非线性方程(组):一维非线性方程(一)二分法、不动点迭代、牛顿法 [MATLAB]
    非线性方程(组):计算基本理论
    常微分方程初值问题:多步预测-修正方法 [MATLAB]
    你会使用super()吗?你确定你了解它吗?
    Django简介
    Web框架的原理
    Django ORM 中的批量操作
    Python的切片
  • 原文地址:https://www.cnblogs.com/leibso-cy/p/11719272.html
Copyright © 2011-2022 走看看