我是cloud3
前几天看到字节跳动技术团队的一篇文章,关于TLB导致的虚拟化场景下“性能抖动"
https://blog.csdn.net/ByteDanceTech/article/details/104765810
今天借着这个话题和大家分享一下CPU内的TLB。
为什么需要TLB
Intel的16位处理器8086共有20根地址线,能访问1MB的内存,是直接通过物理地址来访问内存。CPU发到地址总线上的信号就叫做物理地址。
这样应用程序直接访问物理内存是不安全的,后来从80386开始CPU内引入MMU不仅解决了安全问题,而且使得物理内存按需申请,提升了物理内存的利用率 。
但是使用MMU有个问题:
因为页表是放在内存里的,假如采用4级页表,一次访存仅仅是虚拟地址到物理地址的转换就要去内存查4次页表,再算上真正的内存访问,最坏情况下需要5次内存IO才能获取一个内存数据。
为了提高内存IO时地址转换的效率,现在CPU都引入了TLB。
TLB在哪里
在配备MMU的处理器中,TLB存储在片上RAM中,TLB被组织为一个n路组关联的缓存。每个CPU核心私有TLB,对于SMT架构甚至每个超线程都有自己的TLB。Intel从80386开始引入TLB。
TLB的作用
TLB是Translation Lookaside Buffer的简称,可翻译为“地址转换后援缓冲器”,也可简称为“快表”。
硬件存在TLB后,虚拟地址到物理地址的转换过程发生了变化。MMU接收到虚拟地址后,首先在TLB中查找,如果找到该VA对应的PTE就直接转换,找不到再去内存页表查找,并将虚拟地址和物理地址的映射关系缓存到TLB中。
之前我们提到的高速缓存其实指的是数据cache,数据cache缓存的是内存数据,提高了CPU读取内存数据的速度。而TLB是页表的Cache,缓存的是虚拟地址和其映射的物理地址,调高了MMU的工作效率。
TLB Flush
我们知道进程切换的时候,操作系统会把要运行进程的页目录表物理内存基地址等信息存放到CR3寄存器中,也就是说MMU要查另外的表了,这个时候TLB中缓存的还是上一个进程的页表条目PTE,所以我们需要主动flush掉TLB,这个Flush动作是操作系统加载CR3时触发CPU做的。
当然直接Flush掉整个TLB肯定是没有问题的,但是会有性能问题。
全局和局部Flush
从PTI引入之前,每个进程都有一个属于自己的页表,这个页表中包含两部分内容:用户态地址空间和内核态地址空间。也就是说在用户态和内核态之间进行切换时,是不用切换页表的。
那么TLB中也就会同时缓存内核态和用户态地址(这也是Meltdown漏洞攻击的主要原理)。同时所有进程页表中内核态地址是相同的。
所以,内核态地址映射就可以添加一个Global标识,在进程切换时不被Flush掉。
Intel-64和IA-32 architectures允许使用global pages,是通过CR4.PGE(bit 7)控制的。当global page被使能时,如果PTE中的G flag(bit 8)设置,则相应的TLB entry被视作为global的。
global TLB entry在没有PCID时,进行mov to CR3操作(也就是进程切换时),不会被flush掉。在引入PTI特性之前,所有的内核地址空间对应的映射都被设置为global,如果可以避免在进程切换时flush掉内核态地址空间对应的TLB entry,可以很大程度提升性功能。
用PCID/ASID来区分进程的PTE
Intel在Westmere架构引入进程上下文标识符,即PCID,对应ARM上的ASID。PCID就用于区分TLB中不同进程对应的页表条目PTE。
在Intel的手册中提到:PCID是一个12位的标识符,位于CR3寄存器的最低12位。通过CR4寄存器中的PCIDE Flag来控制是否enable PCID。
在没有PCID的CPU中,当进程切换时,则必须flush掉所有TLB项,有了PCID后每一条PTE中包含PCID信息,新的加载CR3的过程变成了:如果CR4的PCID=1,加载CR3就不需要Flush TLB。
Linux在4.14中版开始引入PCID,4.15版中全面使用了PCID。
TLB shootdown
假如一个进程有多个线程同时运行在不同的CPU上,其中一个线程要释放一部分内存而修改PTE,那这个CPU上的TLB项要修改,同时要通知其他CPU上的TLB要Flush对应的项了,通知的方式是使用IPI (Inter-Processor Interrupt)
这一过程叫做TLB shootdown。
发送IPI的这个过程,在x86上的体现就是需要CPU执行wrmsr指令,对应的操作是触发ICR。wrmsr这条指令在虚拟化上会引发vmexit,需要Hypervisor处理。
做过虚拟化的朋友可能看到vmexit就比较头疼,改造那么多virtio就是为了减少vmexit的,在CPU这块居然还没避免。
如何优化TLB,可以看看文章开头的链接。
我是cloud3,一起聊聊CPU、操作系统、云计算