ARM系列 -- TLB维护指令

文摘   科技   2023-01-22 23:50   河北  
今天来看一条关于TLB维护的指令,无效操作指令TLBI
TLBI指令用于TLB无效操。TLBI指令的编码格式如下:

TLBI的语法格式如下:

TLBI <tlbi_op>{, <Xt>}

关于<tlbi_op>,截取一部分编码。TLBI的操作码比较多,一一介绍的意义不大,感兴趣的话去看Armv8-A架构文档好了。

什么时候需要用到TLBI指令呢?在回答这个问题前,我们先来回忆一下TLB是什么。TLB位于MMUMemory Management Unit)中,如下图。

处理器发出的地址首先要经过地址转换,TLB中存储的是地址转换的页表。TLB本身也是一块cache,所以查询TLB的时候,输入的地址也分为标签位(tag),索引位(index)和偏移位(offset)。与普通的高速缓存不同的是,TLB的输入是地址,输出也是地址。
这里不得不提一下内存管理的基础。
在现代计算机系统中,内存通常都是由操作系统(OS)来管理。在早期的单任务系统中,除去操作系统占用的内存,其它内存都有单一的应用程序占有,这时的内存管理相对简单。
后来发展到多任务系统,原来的独占式管理就不适用了。操作系统发展出段式(Segmentation)内存管理,就是将一个程序按照逻辑单元分成多个程序段,每一个段使用自己单独的虚地址空间。段式内存管理是不连续分配内存技术中的一种。其最大特点在于它按照程序段、数据段等有明确逻辑含义的来分配内存空间,克服了页式的、硬性的、非逻辑划分给保护和共享与支态伸缩带来的不自然性。段的最大好处是可以充分实现共享和保护,便于动态申请内存,管理和使用统一化,便于动态链接;其缺点也很明显,就是容易产生内存碎片,这是因为在系统上运行的程序的各个段的大小往往都不是固定的,而且段的分布也不是连续的。
来操作系统发展出页式(Page)内存管理,页式管理分为静态页式管理和动态页式管理。其基本原理是虚拟空间划分成若干个长度相等的页(page),页式管理把内存空间按页的大小划分成片或者页面(Page Frame),然后把页式虚拟地址内存地址建立一一对应页表,并用相应的硬件地址变换机构,来解决离散地址变换问题。页是内存的最小分配单位,一个应用程序的虚拟页可以存放在任意一个空闲的物理页中。页式内存管理使用页表(Page Table)来进行虚拟地址到物理地址到转换。每个虚拟页都有一个编号,叫做VPNVirtual Page Number);相应的,每个物理页帧也有一个编号,叫做PFNPhysical Frame Number)。页表存储的就是VPNPFN的映射关系。操作系统为每个程序都分配了一个页表,存储在内存当中,页表里由多个页表项(Page Table EntryPTE)组成。页式内存管理的优点是,有效地解决了内存碎片问题,提高了内存利用率。页式内存管理缺点是要求有相应的硬件支持,例如TLB;而且增加了系统开销,页表占用空间大;会有一定的内存浪费,因为通常每个进程申,请的最后一个页不会被充分利用。这里要提一下,关于页的尺寸问题,大页会减少页表的尺寸,比如1GB的地址空间,分成64KB的页,就会有2^30/(64*2^10)=2^14个表项;如果是4KB,就是2^30/(4*2^10)=2^18个表项。大页的代价是内存浪费更严重一些。Armv8-A支持4KB16KB64KB三种页尺寸。
后来又发展出段页式内存管理,即先将用户程序分成若干个段,再把每个段分成若干个页。段页式管理是段式管理和页式管理相结合而成,具有两者的优点。
这里闲聊几句,计算机系统的发展就是不断提出新的创新,然后针对新技术的缺点加以改善。
今天的重点不是讲内存管理,只是为了说明TLB的作用。再回到TLB,既然TLB也是cache,那么也会面临“歧义(ambiguity)”问题。举个例子,假设A进程虚拟地址0x1000映射到物理地址0x2000B进程虚拟地址0x1000映射到物理地址0x3000。当A进程运行时,TLB中的表项是0x1000->0x2000映射。随后切换到B进程的时候,TLB中的表项还存在,但是当B进程访问虚拟地址0x1000时将得到物理地址0x2000的数据,而不是物理地址0x3000的数据。想要避免歧义的发生,当切换进程的时候,操作系统或应用程序需要将TLB全部刷新一遍,将旧的的表项无效掉。这样,新进程需要重新加载地址页表,解决了歧义问题。
但是,每次进程切换都无效所有TLB表项的做法也会带来问。对新进程来说,初始会面对一个空白的TLB,也就是会有大量的TLB未命中和高速缓存未命中,处理器性能会大打折扣。为了解决这一问题,引入了进程地址空间IDAddress Space IDASID),处理器发给TLB的不仅仅是虚拟地址,还带有进程信息。在访问TLB时,就不仅仅是地址匹配,还加上了ASID匹配。有了ASID机制,在进程切换的时候就不需要将旧进程的地址页表刷新出去。再来看上一个例子,如果A进程和B进程有不同的ASID,在切换到B进程后,虽然TLB中存在虚拟地址0x1000的表项,但是这个表项被标记为A进程的ASIDB进程查询TLB时虽然可以匹配上虚拟地址,但是ASID确匹配不上。因此B进程的这次查询的结果是TLB未命中,需要重新加载0x1000->0x3000的地址转换,并标记上B进程的ASID
ASID机制虽然可以减少进程切换的TLB未命中,但是还不足够有效。设想一种场景,A进程运行了很长一段实际,基本上TLB中的表项全部标记为A进程的ASID,当切换到B进程后,依然会有大量的TLB未命中发生。其实,有一些地址转换对所有的进程是相同的,这部分就不需要用ASID做标记位来区分。比如,内核空间就是所有进程共享的。这时,可以为TLB再增加一个标志位nG,以区分TLB表项是全局的还是进程特有的。在进程切换的时候,全局的表项就不需要无效了。
除了ASIDArmv8-A还为虚拟化提供了VMIDVirtual Machine ID),用来标识虚拟机。这部分在前面介绍ARM虚拟化的文章中有提到,就不再赘述了。
总结一下今天的内容,为了操作系统管理内存,引入了TLB;在进程切换时,TLB会产生“歧义”问题,因此每次进程切换需要无效上一个进程的TLB表项;为了减少进程切换造成的处理器性能损失,引入了ASIDnG
今天就到这里吧,是不是没用的知识又增加了一些呢?

老秦谈芯
交流ASIC技术