Linux X86下的TLB机制分析

TLB - translation lookaside buffer

快表,直译为翻译后备缓冲器,也可以理解为页表缓冲,地址变换高速缓存。

由于页表存放在主存中,因此程序每次访存至少需要两次:一次访存获取物理地址,第二次访存才获得数据。提高访存性能的关键在于依靠页表的访问局部性。当一个转换的虚拟页号被使用时,它可能在不久的将来再次被使用到,。

TLB是一种高速缓存,内存管理硬件使用它来改善虚拟地址到物理地址的转换速度。当前所有的个人桌面,笔记本和服务器处理器都使用TLB来进行虚拟地址到物理地址的映射。使用TLB内核可以快速的找到虚拟地址指向物理地址,而不需要请求RAM内存获取虚拟地址到物理地址的映射关系。这与data cache和instruction caches有很大的相似之处。

TLB原理

当cpu要访问一个虚拟地址/线性地址时,CPU会首先根据虚拟地址的高20位(20是x86特定的,不同架构有不同的值)在TLB中查找。如果是表中没有相应的表项,称为TLB miss,需要通过访问慢速RAM中的页表计算出相应的物理地址。同时,物理地址被存放在一个TLB表项中,以后对同一线性地址的访问,直接从TLB表项中获取物理地址即可,称为TLB hit。

想像一下x86_32架构下没有TLB的存在时的情况,对线性地址的访问,首先从PGD中获取PTE(第一次内存访问),在PTE中获取页框地址(第二次内存访问),最后访问物理地址,总共需要3次RAM的访问。如果有TLB存在,并且TLB hit,那么只需要一次RAM访问即可。

TLB表项

TLB内部存放的基本单位是页表条目,对应着RAM中存放的页表条目。页表条目的大小固定不变的,所以TLB容量越大,所能存放的页表条目越多,TLB hit的几率也越大。但是TLB容量毕竟是有限的,因此RAM页表和TLB页表条目无法做到一一对应。因此CPU收到一个线性地址,那么必须快速做两个判断:

1 所需的也表示否已经缓存在TLB内部(TLB miss或者TLB hit)

2 所需的页表在TLB的哪个条目内

为了尽量减少CPU做出这些判断所需的时间,那么就必须在TLB页表条目和内存页表条目之间的对应方式做足功夫

全相连 - full associative

在这种组织方式下,TLB cache中的表项和线性地址之间没有任何关系,也就是说,一个TLB表项可以和任意线性地址的页表项关联。这种关联方式使得TLB表项空间的利用率最大。但是延迟也可能相当的大,因为每次CPU请求,TLB硬件都把线性地址和TLB的表项逐一比较,直到TLB hit或者所有TLB表项比较完成。特别是随着CPU缓存越来越大,需要比较大量的TLB表项,所以这种组织方式只适合小容量TLB

直接匹配

每一个线性地址块都可通过模运算对应到唯一的TLB表项,这样只需进行一次比较,降低了TLB内比较的延迟。但是这个方式产生冲突的几率非常高,导致TLB miss的发生,降低了命中率。

比如,我们假定TLB cache共包含16个表项,CPU顺序访问以下线性地址块:1, 17 , 1, 33。当CPU访问地址块1时,1 mod 16 = 1,TLB查看它的第一个页表项是否包含指定的线性地址块1,包含则命中,否则从RAM装入;然后CPU方位地址块17,17 mod 16 = 1,TLB发现它的第一个页表项对应的不是线性地址块17,TLB miss发生,TLB访问RAM把地址块17的页表项装入TLB;CPU接下来访问地址块1,此时又发生了miss,TLB只好访问RAM重新装入地址块1对应的页表项。因此在某些特定访问模式下,直接匹配的性能差到了极点

组相连 - set-associative

为了解决全相连内部比较效率低和直接匹配的冲突,引入了组相连。这种方式把所有的TLB表项分成多个组,每个线性地址块对应的不再是一个TLB表项,而是一个TLB表项组。CPU做地址转换时,首先计算线性地址块对应哪个TLB表项组,然后在这个TLB表项组顺序比对。按照组长度,我们可以称之为2路,4路,8路。

经过长期的工程实践,发现8路组相连是一个性能分界点。8路组相连的命中率几乎和全相连命中率几乎一样,超过8路,组内对比延迟带来的缺点就超过命中率提高带来的好处了。

这三种方式各有优缺点,组相连是个折衷的选择,适合大部分应用环境。当然针对不同的领域,也可以采用其他的cache组织形式。

TLB表项更新

TLB表项更新可以有TLB硬件自动发起,也可以有软件主动更新

1. TLB miss发生后,CPU从RAM获取页表项,会自动更新TLB表项

2. TLB中的表项在某些情况下是无效的,比如进程切换,更改内核页表等,此时CPU硬件不知道哪些TLB表项是无效的,只能由软件在这些场景下,刷新TLB。

在linux kernel软件层,提供了丰富的TLB表项刷新方法,但是不同的体系结构提供的硬件接口不同。比如x86_32仅提供了两种硬件接口来刷新TLB表项:

1. 向cr3寄存器写入值时,会导致处理器自动刷新非全局页的TLB表项

2. 在Pentium Pro以后,invlpg汇编指令用来无效指定线性地址的单个TLB表项无效。

TLB刷新机制

在MMU开启的情形下,线性地 址到物理地址的转换需要经过页表的查找,如果每次都这么做的话显然对系统性能有影响,因此出现了这么一个cache,用来将已经此前的查找结果保存在这个 TLB中。显然TLB因为容量的限制不可能将所有的线性地址到物理地址的转换全部容纳进去,当TLB中所有的entry都被放置满的时候,处理器会决定将
一个旧的entry替换掉。

TLB本质上就是一块cache,所以也存在cache一致性的问题,比如如果操作系统修改了页表中的某一项 的映射关系,如果该项的映射恰好保存在TLB中,那么就出现了一致性的问题。与x86下系统物理内存与处理器间的cache一致性不一样,TLB的一致性 需要系统软件出面解决,而不是硬件。x86为此提供了两种方式来解决TLB一致性的问题:

1. 更新CR3. 如果cr3寄存器被重新加载,那么会导致整个TLB无效,OS可以通过比如: mov eax, cr3; move cr3, eax;这样的指令来刷新整个TLB。还有,我们知道Linux下进程切换时,会重新加载cr3寄存器,因为新老进程的页表项是不相同的,因此需要使 TLB无效防止出现不一致的情况。

2. x86提供了一条INVLPG指令,该指令是特权指令。操作系统可以通过该指令对TLB中的单独的某一entry进行更新。关于这条指令的详细信息,可以 参考一下 intel 64 and IA32 Architecture Software Developer‘s Manual. Linux kernel中使用到该指令的例子有:

<arch/x86/mm/pgtable.c>

void set_pte_vaddr(unsigned long vaddr, pte_t pteval)

{

...

/*

* It‘s enough to flush this one mapping.

* (PGE mappings get flushed as well)

*/

__flush_tlb_one(vaddr);

}

set_pte_vaddr函数在内核中被用来直接操作页目录表项,里面涉及到x86线性地址映射物理地址的原理,函数在最后调用__flush_tlb_one来刷新TLB中对应的项:

<arch/x86/mm/pgtable_32.c>

static inline void __flush_tlb_one(unsigned long addr)

{

if (cpu_has_invlpg)

__flush_tlb_single(addr);

else

__flush_tlb();

}

从函数的实现可以看到,如果CPU支持INVLPG指令,那么就调用 __flush_tlb_single函数来刷新TLB中的项(具体哪一项由虚拟线性地址addr来指定),__flush_tlb_single的实现是:

static inline void __native_flush_tlb_single(unsigned long addr)

{

asm volatile("invlpg (%0)" ::"r" (addr) : "memory");

}

上面是一个非常直观的invlpg指令使用范例。在__flush_tlb_one函数中,我们还可以看到__flush_tlb的调用,后者被用来刷新整个TLB,其实就是本文前面提到的用mov指令倒换一下重写cr3.

在分页机制打开的情形下,当CPU要访问一个线性地址时,首先会查找TLB,如果该线性地址的转换已经存放在TLB中了,那么直接取得物理地址了(实际上TLB中存放的是VPN和PPN的映射),此时不需要去查页目录页表了。

如果当前线性地址的映射尚没有保存在TLB中,那么就发生了一次TLB miss,此时需要查找页目录和页表项,然后把映射结果记录到TLB中。

时间: 2024-11-10 08:03:06

Linux X86下的TLB机制分析的相关文章

Linux x86_64 APIC中断路由机制分析

不同CPU体系间的中断控制器工作原理有较大差异,本文是<Linux mips64r2 PCI中断路由机制分析>的姊妹篇,主要分析Broadwell-DE X86_64 APIC中断路由原理.中断配置和处理过程,并尝试回答如下问题: 为什么x86中断路由使用IO-APIC/LAPIC框架,其有什么价值? pin/irq/vector的区别.作用,取值范围和分配机制? x86_64 APIC关键概念 Pin 此处的pin特指APIC的中断输入引脚,与内外部设备的中断输入信号相连.从上图中可以看出,

Linux mips64r2 PCI中断路由机制分析

Linux mips64r2 PCI中断路由机制分析 本文主要分析mips64r2 PCI设备中断路由原理和irq号分配实现方法,并尝试回答如下问题: PCI设备驱动中断注册(request_irq)时的irq#从哪里来?是硬件相关?还是软件相关? 中断上报时,CPU是如何获得这个irq#的? 本文主要分析PIC(可编程中断控制器)的工作原理,PIC一般集成在CPU中,不同arch.vendor CPU的PIC实现原理也不尽相同.本文基于kerne3.10 + mips64r2 XXX CPU分

Linux环境下的CPU消耗分析

在Linux系统中, CPU 主要用于中断,内核以及用户进程的任务处理,优先级为 中断 > 内核 > 用户进程.在CPU消耗分析中,我们还经常遇到下面几个概念. 上下文切换         每个CPU在同一时间只能执行一个线程, Linux 中线程是抢占式调度的. 也就是说每个线程分配一定的执行时间,当到达执行时间,线程中有IO阻塞或者高优先级线程要执行时,Linux将切换执行的线程,在切换时要存储目前线程的的执行状态,并恢复要执行的线程的状态,这个过程就称为上下文切换.在java 应用程序中

Linux内核抢占实现机制分析【转】

Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介绍了内核抢占和用户抢占的概念和区别,接着分析了不可抢占内核的特点及实时系统中实现内核抢占的必要性.然后分析了禁止内核抢占的情况和内核抢占的时机,最后介绍了实现抢占内核所做的改动以及何时需要重新调度. [关键字]内核抢占,用户抢占,中断, 实时性,自旋锁,抢占时机,调度时机,schedule,pree

Linux内核抢占实现机制分析

Sailor_forever  [email protected] 转载请注明 http://blog.csdn.net/sailor_8318/archive/2008/09/03/2870184.aspx [摘要]本文详解了Linux内核抢占实现机制.首先介绍了内核抢占和用户抢占的概念和区别,接着分析了不可抢占内核的特点及实时系统中实现内核抢占的必要性.然后分析了禁止内核抢占的情况和内核抢占的时机,最后介绍了实现抢占内核所做的改动以及何时需要重新调度. [关键字]内核抢占,用户抢占,中断, 

linux RCU锁机制分析

openVswitch(OVS)源代码之linux RCU锁机制分析 分类: linux内核  |  标签: 云计算,openVswitch,linux内核,RCU锁机制  |  作者: yuzhihui_no1 相关  |  发布日期 : 2014-10-19  |  热度 : 1044° 前言 本来想继续顺着数据包的处理流程分析upcall调用的,但是发现在分析upcall调用时必须先了解linux中内核和用户空间通信接口Netlink机制,所以就一直耽搁了对upcall的分析.如果对ope

Linux内核态抢占机制分析(转)

Linux内核态抢占机制分析  http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive Kernel)和可抢占式内核(Preemptive Kernel)的区别.接着分析Linux下有两种抢占:用户态抢占(User Preemption).内核态抢占(Kernel Preemption).然后分析了在内核态下:如何判断能否抢占内核(什么是可抢占的条件):何时触发重新调度(何时设置可抢

Linux 线程实现机制分析 Linux 线程实现机制分析 Linux 线程模型的比较:LinuxThreads 和 NPTL

Linux 线程实现机制分析 Linux 线程实现机制分析  Linux 线程模型的比较:LinuxThreads 和 NPTL http://www.ibm.com/developerworks/cn/linux/kernel/l-thread/ 自从多线程编程的概念出现在 Linux 中以来,Linux 多线应用的发展总是与两个问题脱不开干系:兼容性.效率.本文从线程模型入手,通过分析目前 Linux 平台上最流行的 LinuxThreads 线程库的实现及其不足,描述了 Linux 社区是

MinHook测试与分析(x86下 E8,E9,EB,CALL指令测试,且逆推测试微软热补丁)

依稀记得第一次接触Hook的概念是在周伟民先生的书中-><<多任务下的数据结构与算法>>,当时觉得Hook的本质就是拦截,就算到现在也是如此认为. 本篇文章是在x86下测试与分析跳转+offset类型的Hook,并且逆推测出热补丁的简单用法,MinHook它的中心就是覆盖重写并且可以复原.知道大概的思路后后让我们先来具体的实现MinHook再去做测试. 首先是堆的申请,这是必要也必须做的,对于微软函数HeapCreate()就不再赘述,以下是实现与卸载 1 NTSTATUS