访问内存之后,后面执行两句代码后;并不能保证刚才访问的代码还在TLB中;有可能被刷新出去了;
实验验证缺陷:
代码 不连续 TLB 被淘汰:
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位即使存在 访问代码不连续也可能被淘汰:
我这里循环了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 流水线 技术( 执行指令的时候,也在取指令,且根据将会执行的可能性提前取指令):
现象:
没有 可执行属性的时候;且没有主动加入快表的时候;坑定 是可预计的数据:
但是 一旦加入了可执行属性,流水线的预先取指令,可能执行到这儿,取后面的指令:
发现后面的指令有 0x405000 ,而且 0x405000 有可执行属性,可能就预先取了这个指令;继而访问;加入了TLB。 (但是即使这样这里也是指令TLB ,但是这里影响到了 数据TLB,但是概率也不高)
代码:
#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");}