16_TLB与流水线

て烟熏妆下的殇ゞ 提交于 2019-12-01 22:38:04

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//0x401000void _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 中存在所以应该还是 1PTE(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~ 00081000void 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\n", 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//0x401000void _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 中存在所以应该还是 1PTE(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~ 00081000void 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\n", 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//0x401000void _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 00081000void 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\n",i, g_out);    }system("pause");}​​


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!