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

访问内存之后,后面执行两句代码后;并不能保证刚才访问的代码还在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
//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\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
//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\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
//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\n",i, g_out);

}
system("pause");
}

16_TLB与流水线的更多相关文章

  1. CI-持续集成(1)-软件工业“流水线”概述

    CI-持续集成(1)-软件工业“流水线”概述 1   概述 持续集成(Continuous integration)是一种软件开发实践,即团队开发成员经常集成它们的工作,通过每个成员每天至少集成一次, ...

  2. 【GoLang】golang 的精髓--流水线,对现实世界的完美模拟

    直接上代码: package main import ( "fmt" "runtime" "strconv" "sync" ...

  3. android so调试时遇到的坑 - arm流水线

    直接看下面这段ARM汇编: 此时运行到的代码为ADD R3,PC 此时看一下寄存器窗口的值: 按理来说执行完ADD R3,PC后的效果应该是R3=R3+PC ,R3=40A1D5C8 但是我们可以执行 ...

  4. 【转载】关于OpenGL的图形流水线

    本文转载自 http://blog.csdn.net/racehorse/article/details/6593719 GLSL教程 这是一些列来自lighthouse3d的GLSL教程,非常适合入 ...

  5. .Net中的并行编程-5.流水线模型实战

    自己在Excel整理了很多想写的话题,但苦于最近比较忙(其实这是借口).... 上篇文章<.Net中的并行编程-4.实现高性能异步队列>介绍了异步队列的实现,本篇文章介绍我实际工作者遇到了 ...

  6. Intel系列CPU的流水线技术的发展

    Intel系列CPU的流水线技术的发展 CPU(Central processing Unit),又称“微处理器(Microprocessor)”,是现代计算机的核心部件.对于PC而言,CPU的规格与 ...

  7. Verilog学习笔记设计和验证篇(一)...............总线和流水线

    总线 总线是运算部件之间数据流通的公共通道.在硬线逻辑构成的运算电路中只要电路的规模允许可以比较自由的确定总线的位宽,从而大大的提高数据流通的速度.各个运算部件和数据寄存器组可以通过带有控制端的三态门 ...

  8. ARM流水线关键技术分析与代码优化

    引 言    流水线技术通 过多个功能部件并行工作来缩短程序执行时间,提高处理器核的效率和吞吐率,从而成为微处理器设计中最为重要的技术之一.ARM7处理器核使用了典型三级流 水线的冯·诺伊曼结构,AR ...

  9. TMS320C54x系列DSP的CPU与外设——第8章 流水线

    第8章 流水线 本章描述了TMS320C54x DSP流水线的操作,列出了对不同寄存器操作时的流水线延迟周期.(对应英语原文第7章) 8.1 流水线操作 TMS320C54x DSP有一个6段的指令流 ...

随机推荐

  1. java虚拟机规范(se8)——java虚拟机的编译(二)

    3.3 算术运算 java虚拟机通常在操作数栈上进行算术运算(例外情况是iinc指令,它直接增加一个局部变量的值).例如下面的align2grain()方法,它的作用是将int值对齐到2的指定次幂: ...

  2. 实用的Python(2)利用Python制作gif动图

    一.简介 moviepy是一个专门用于视频剪辑制作的模块,可以自动化完成很多繁琐的视频剪辑处理工作,除了处理视频数据之外,moviepy中还内置了可以制作gif动图的功能,通过使用moviepy.ed ...

  3. C# 线程池的使用 终止线程池中的队列

    C#的线程池使用起来还是非常简单的,这里记录一下. 根据http://blog.csdn.net/chen_zw/article/details/7939834里的描述这里记录一下C#线程池的特点 一 ...

  4. 利用HTML和CSS实现常见的布局

    水平居中的页面布局中最为常见的一种布局形式,多出现于标题,以及内容区域的组织形式,下面介绍四种实现水平居中的方法(注:下面各个实例中实现的是child元素的对齐操作,child元素的父容器是paren ...

  5. JS模拟实现题目(new debounce throwee 等)

    模拟new实现 function newObject() { let obj = new Object(); let Con = [].shift.apply(arguments) obj.__pro ...

  6. 基于Libpcap实现一个网络数据包嗅探器

    基本功能就是来捕获所有流经本网卡的数据包. 实现流程: 查找网络设备 打开网络设备 查找设备信息 输入过滤规则 编译输入规则 设置输入规则 开始捕获数据包 调用数据包分析模块 输出MAC,IP,协议以 ...

  7. MySQL不支持事务处理的解决方法

    MySQL数据库默认的存储引擎类型是MyISAM,这种存储引擎类型不支持事务处理. 在MySQL中,只有InnoDB存储引擎类型的数据表才能支持事务处理. 因此,如果想让MySQL支持事务处理,只要将 ...

  8. java程序中的多线程(转)

    为什么会排队等待? 下面的这个简单的 Java 程序完成四项不相关的任务.这样的程序有单个控制线程,控制在这四个任务之间线性地移动.此外,因为所需的资源 ― 打印机.磁盘.数据库和显示屏 -- 由于硬 ...

  9. JS数组 Array

    1.创建数组 var array=new Array(); 2.添加数组 array.push("111"); array.push("111"); array ...

  10. Python读取文件时出现UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position xx: 解决方案

    Python在读取文件时 with open('article.txt') as f: # 打开新的文本 text_new = f.read() # 读取文本数据 出现错误: UnicodeDecod ...