avatar

Catalog
TLB,控制寄存器

保护模式的内容接近尾声,这一篇文章补充一下琐碎的知识点,下面先从TLB开始

TLB

设计原因

  1. 假设我们通过一个线性地址访问一个物理页,想要去读取物理页上某个字节。但是实际过程中,并非只读了1个字节,我们需要先读取PDE,再读取PTE,最后再读取存放1个字节的物理页,读取的内容远远超过1个字节了。
  2. 在2-9-9-12分页下,会多读24个字节,如果读取的内容跨页了(存在两个不同的物理页上),那多读的字节会更多

为了提高效率,只能通过做记录来进行弥补。

因此CPU内部设计了一个表,用来做记录;由于位于CPU内部,速度和寄存器一样快,当然,表也不能做的过大。这个表叫做TLB(Translation Lookaside Buffer),用于地址解析

TLB结构

LA(线性地址) PA(物理地址) ATTR(属性) LRU(统计)
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx

说明:

  • ATTR(属性):PAE分页,用PDPE&PDE&PTE。10-10-12分页则PDE&PTE
  • 不同CPU的TLB表大小不一样
  • 只要Cr3改变了(说明进程切换了),先前的TLB则会失效,换一套新的TLB,一核一套TLB

由于操作系统中的高2G映射基本不变,如果Cr3改了,直接刷新TLB,对于重建高2G以上的对应关系很浪费,所PDE和PTE中有个标志位G位,刷新TLB时将不会刷新PDE/PTE的G位为1的页。若TLB满了,则CPU会根据统计信息将不常用的地址废弃,保留最近最常用的

注意:只有当PDE的PS位为1时(即当前物理页为大页),G位才有效。

TLB种类

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

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

TLB验证

呵呵,这个破实验花了我一下午,我真是太菜了,一个原因是0地址挂物理页,踩了好几次坑,第二个是VC6很多强转不支持,耽误了很多时间。给0地址挂物理页的步骤就不赘述了,这里采用的10-10-12分页,只是采用了代码挂物理页的方式,具体可以参考基址小实验这一篇,这里就讲讲验证的过程。

我们先给0地址挂上第一个地址(0x425000,这是我随便选的,选错了可能蓝屏)的物理页,然后取0地址的处的置,发现值为0。

这时,我们注释掉给0地址挂第一个地址(0x425000)的物理页的代码,并给0地址挂第二个地址(0x426000)的物理页。这时再取0地址处的值,发现值为0x43,可以发现,这两个线性地址所对应的物理页上的值是不同的。

这时我们把上面的注释拿掉,先给0地址挂第一个地址的物理页,然后再给0地址挂第二个地址的物理页,按照道理,这时我们取到的值应该是第二个地址对应物理页上的值,我们来查看结果:

神奇的事情发生了,我们取到的仍然是第一个地址对应物理页上的值,这其实就是TLB的作用。

这时,我们增加一条语句

Code
1
invlpg dword ptr ds:[0]

再次运行程序发现,仅仅多了这一条语句,读取0地址的值,就变成了第二个地址对应物理页上的值,Invlpg是让指定页TLB无效化的指令,因此再次访问时,原先的TLB已经被废弃,就需要重新去物理页读取,此时0地址对应的物理页已经是第二个地址的物理页了。当然,除了使用Invlpg指令,修改Cr3也可以做到让TLB无效化。

下面附上完整代码

c
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
#include "stdafx.h"
#include

DWORD phyAddr, phyAddr2, temp;

__declspec(naked) Test() {
_asm {
pushad
pushfd
}
phyAddr = (DWORD)(0xc0000000 + ((0x425000 >> 0xa) & 0x3ffffc));
phyAddr2 = (DWORD)(0xc0000000 + ((0x426000 >> 0xa) & 0x3ffffc));

_asm{
mov eax, phyAddr
mov eax, [eax]
mov dword ptr ds:[0xc0000000], eax
mov eax, dword ptr ds:[0]
mov temp, eax

// invlpg dword ptr ds:[0] 无效化指定页的TLB
// mov eax, cr3 切换Cr3来清空TLB
// mov cr3, eax

mov eax, phyAddr2
mov eax, [eax]
mov dword ptr ds:[0xc0000000], eax
mov eax, dword ptr ds:[0]
mov temp, eax
}

_asm{
popfd
popad
retf
}
}

int main(int argc, char* argv[])
{
char buffer[] = {0, 0, 0, 0, 0x4B, 0};
_asm call fword ptr buffer

printf("temp: %x", temp);
getchar();
return 0;
}

控制寄存器

说完了TLB,来说说控制寄存器。控制寄存器的作用主要是用于控制和确定CPU的操作模式。主要包括Cr0,Cr1,Cr2,Cr3,Cr4,其中Cr1保留。

Cr0寄存器

Cr0寄存器,主要包括一些控制操作系统模式以及处理器状态的控制标志位。

这里介绍几个主要的标志位,其余位的描述可以参考Intel白皮书第三卷系统架构综述那章。

  1. PE:Cr0下标为0的位是启用保护(Protection Enable)标志。PE=1保护模式,PE=0实地址模式,这个标志仅开启段级保护,而并没有启用分页机制。若要启用分页机制,那么PE和PG标志都要置位。

  2. PG:当设置该位时即开启了分页机制。在开启这个标志之前必须已经或者同时开启PE标志

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

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

Cr2寄存器

Cr2寄存器,保存导致缺页异常的线性地址。

之前在中断与异常中,简要概括了缺页异常,当CPU访问某个无效页面,会产生缺页异常,此时,CPU会将引起异常的线性地址存放在Cr2中,以便操作系统处理完缺页异常后,返回到原本执行的位置继续执行。

Cr3寄存器

Cr3我们太熟悉了,在10-10-12分页是页目录表基址,在PAE分页下,则是页目录指针表基址

这里有两个属性,PWT和PCD之前在页的部分一直没有讲,在介绍之前,先来了解一个概念,叫做CPU缓存

CPU缓存

  1. CPU缓存是位于CPU与物理内存之间的临时存储器,它的容量比内存小的多,但是交换速度远快于内存。
  2. CPU缓存可以做的很大,从几K,几十K,几百K,甚至上M。
  3. CPU缓存与TLB的区别:
    • TLB:线性地址 <—–> 物理地址
    • CPU缓存: 物理地址 <—–> 内存

有了CPU缓存和TLB的概念后,就可以来讲讲PWT和PCD这俩属性了。

PWT(Page Write Through)

  • PWT = 1时,CPU向cache写入数据时,同时向memory也写一份,使cache和memory的数据保持一致。优点是简单,缺点是每次都要访问memory,速度比较慢,即Write Through。
  • PWT = 0时,CPU向cache写入数据时,不将数据写入内存中,分为两种情况:
    1. Post Write:CPU更新cache数据时,把更新的数据写入到一个更新缓冲器,在合适的时候才对memory进行更新。这样可以提高cache访问速度,但是,在数据连续被更新两次以上的时候,缓冲区将不够使用,被迫同时更新memory。
    2. Write Back:CPU更新cache时,只是把更新的cache区标记一下,并不同步更新memory。只是在cache区要被新进入的数据取代时,才更新memory。这样做的原因是考虑到很多时候cache存入的是中间结果,没有必要同步更新memory。优点是CPU执行的效率提高,缺点是实现起来技术比较复杂。

PCD(Page Cache Disable)

  • PCD = 1时,禁止某个页写入缓存,直接写入内存。例如,做页表用的页,已经存储在TLB中了,可能不需要再缓存了。
  • PCD = 0时,不限制页写入缓存,可以参考上面PWT的情况。

Cr4寄存器

Cr2寄存器,保存了一组启用多种架构扩展的标志位

这里简单概括一下PAE位和PSE位:

  • PAE:置1时,是PAE分页;置0时,是10-10-12分页。之前在boot.ini中设置execute/noexecute的作用就是修改PAE位
  • PSE:控制PDE中PS位的开关,当PSE置1时,PS位才有效。具体如下:

控制寄存器小节

除了上述介绍的,还有一个Cr8寄存器,仅仅在64位下才存在,这里就不作介绍了,其余寄存器总览如下:

参考文章1:https://blog.csdn.net/wyzxg/article/details/7254458

参考文章2:https://blog.csdn.net/q1007729991/article/details/53000410

参考教程:https://www.bilibili.com/video/BV1NJ411M7aE?p=33

Author: cataLoc
Link: http://cata1oc.github.io/2020/03/24/TLB%EF%BC%8C%E6%8E%A7%E5%88%B6%E5%AF%84%E5%AD%98%E5%99%A8/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶