介绍TLB以前,咱们先来回顾一个操做系统里的基本概念,虚拟内存。php
在用户的视角里,每一个进程都有本身独立的地址空间,A进程的4GB和B进程4GB是彻底独立不相关的,他们看到的都是操做系统虚拟出来的地址空间。可是呢,虚拟地址最终仍是要落在实际内存的物理地址上进行操做的。操做系统就会经过页表的机制来实现进程的虚拟地址到物理地址的翻译工做。其中每一页的大小都是固定的。这一段我不想介绍的太过于详细,对这个概念不熟悉的同窗回去翻一下操做系统的教材。 redis
页表管理有两个关键点,分别是页面大小和页表级数缓存
在Linux下,咱们经过以下命令能够查看到当前操做系统的页大小数据结构
# getconf PAGE_SIZE 4096
能够看到当前个人Linux机器的页表是4KB的大小。ide
2.页表级数函数
为了帮助你们回忆这段知识,我举个例子。若是想支持32位的操做系统下的4GB进程虚拟地址空间,假设页表大小为4K,则共有2的20次方页面。若是采用速度最快的1级页表,对应则须要2的20次方个页表项。一个页表项假如4字节,那么一个进程就须要(1048576*4=)4M的内存来存页表项。
若是是采用2级页表,如图1,则建立进程时只须要有一个页目录就能够了,占用(1024*4)=4KB的内存。剩下的二级页表项只有用到的时候才会再去申请。 工具
如今的操做系统须要支持的但是48位地址空间(理论上能够支持64位,但其实如今只支持到了48位,也足够用了),并且要支持成百上千的进程,若是不采用分级页表的方式,则建立进程时就须要为其维护一个2的36次方个页表项(64位Linux目前只使用了地址中的48位的,在这里面,最后12位都是页内地址,只有前36位才是用来寻找页表的), 2^36 *4Byte=32GB,这个更不能忍了。 也必须和32位系统同样,进一步提升页表的级数。 性能
Linux在v2.6.11之后,最终采用的方案是4级页表,分别是:spa
这样,一个64位的虚拟空间,初始建立的时候只须要维护一个2^9 大小的一个页全局目录就够了,如今的页表数据结构被扩展到了8byte。这个页全局目录仅仅须要(2^9 *8=)4K,剩下的中间页目录、页表项只须要在使用的时候再分配就行了。Linux就是经过这种方式支持起(2^48 =)256T的进程地址空间的。操作系统
上面终于费劲扒了半天Linux虚拟内存的实现,我终于能够开始说我想说的重点了。
虽然建立一个支持256T的地址空间的进程在初始的时候只须要4K的页全局目录,可是,这也带来了额外的问题,页表是存在内存里的。那就是一次内存IO光是虚拟地址到物理地址的转换就要去内存查4次页表,再算上真正的内存访问,最坏状况下须要5次内存IO才能获取一个内存数据!!
和CPU的L一、L二、L3的缓存思想一致,既然进行地址转换须要的内存IO次数多,且耗时。那么干脆就在CPU里把页表尽量地cache起来不就好了么,因此就有了TLB(Translation Lookaside Buffer),专门用于改进虚拟地址到物理地址转换速度的缓存。其访问速度很是快,和寄存器至关,比L1访问还快。
我原本想实际看一下TLB的信息,但翻遍了Linux的各类命令,也没有找到像sysfs这么方便查看L一、L二、L3大小的方法。仅仅提供下图供你们参考吧! (谁要是找到了查看TLB的命令,别忘了分享给飞哥啊,谢谢!)
有了TLB以后,CPU访问某个虚拟内存地址的过程以下
因为第2步是相似于寄存器的访问速度,因此若是TLB能命中,则虚拟地址到物理地址的时间开销几乎能够忽略。若是想了解TLB更详细的工做机制,请参考《深刻理解计算机系统-第9章虚拟内存》
既然TLB缓存命中很重要,那么有什么工具可以查看你的系统里的命中率呢? 还真有
# perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses -p $PID Performance counter stats for process id '21047': 627,809 dTLB-loads 8,566 dTLB-load-misses # 1.36% of all dTLB cache hits 2,001,294 iTLB-loads 3,826 iTLB-load-misses # 0.19% of all iTLB cache hits
由于TLB并非很大,只有4k,并且如今逻辑核又形成会有两个进程来共享。因此可能会有cache miss的状况出现。并且一旦TLB miss形成的后果可比物理地址cache miss后果要严重一些,最多可能须要进行5次内存IO才行。建议你先用上面的perf工具查看一下你的程序的TLB的miss状况,若是确实不命中率很高,那么Linux容许你使用大内存页,不少大牛包括PHP7做者鸟哥也这样建议。这样将会大大减小页表项的数量,因此天然也会下降TLB cache miss率。所要承担的代价就是会形成必定程度的内存浪费。在Linux里,大内存页默认是不开启的。
开发内功修炼之CPU篇专辑:
个人公众号是「开发内功修炼」,在这里我不是单纯介绍技术理论,也不仅介绍实践经验。而是把理论与实践结合起来,用实践加深对理论的理解、用理论提升你的技术实践能力。欢迎你来关注个人公众号,也请分享给你的好友~~~