首先介绍一下SPARC的内存管理单元。我们知道x86处理器采用了多级页表机制,在x64上最多可以有4级。其实早期32位SPARC体系架构,如sun4m和sun4d,也是这种机制。不过后来的sun4u体系架构采用了一种叫做hashed page tables的技术,这是对IBM System/38 inverted page table的扩展。这种技术的思想很简单:SPARC体系架构的页是8K,将每一个建立的地址空间页到物理内存页的映射放到hash表中,其中用户地址空间和内核地址空间是分开放的,也就是两个全局哈希表,uhem_hash和khme_hash。如果需要虚拟地址到物理地址的转换,就要在这两个哈希表中查找。这样就避免了多级页表带来的物理内存的浪费。如果每次转换都要查找哈希表,显然会比多级页表慢。为此Solaris为每个进程都创建了translation storage buffer(TSB)。TSB用来保存那些最近用过的映射。而TSB中最近用过的映射又会由软件写到TLB中。由此可见,SPARC内存管理单元是在软件的协助下完成的。对x86来说,CR3寄存器指向页表基地址,有了这个地址,虚拟地址到物理地址的转换就由硬件完成了(当然页表项中的映射也是由系统软件写进去的,这里指的是多级页表的查找过程)。对SPARC和Solaris来,如果要查找一个虚拟地址到物理地址的映射就要首先看在不在TLB中,如果不在,看在不在TSB中,如果不在,看在不在哈希表中,如果还不在就要分配物理内存创建一个新的映射,先放到哈希表中,然后放到TSB中,最后写到TLB中,这些都是由软件完成的。
SPARC内存管理单元对于地址空间分散的进程会节省大量的物理内存,但是进程退出回收内存时会非常耗时,因为对地址空间中每一个8K大小的页面几乎都要查找哈希表来确定相应的物理页以回收内存。为此SPARC又引进了Shadow HME Blocks。这其实巧妙的构成了一个树形结构,每8个连续的8K地址空间汇聚成一个64K的映射,如果这8个地址空间有任何一个映射,就要在这个64K的映射中用shadow flag标记,否则不标记,然后8个连续的64K地址空间又汇聚成一个512K的映射,以此类推,最大的步进可以达到4M或者256M。如果一个256M的地址空间没有标记,就说明这256M地址空间没有任何映射,可以检查下一个256M了,如果有标记,说明至少有一个8K的映射,则需要进一步细化逐步检查。这些Shadow HME和HME一样也是存在哈希表中的。
SPARC的translation table entry(TTE)和x86的page table entry(PTE)最大区别在于其tag不同。SPARC的TTE的tag包含了13位的Context ID,可以区分不同的地址空间,也就是说不同地址空间的TTE可以在TLB中共存。我们知道对x86处理器来说,每一次地址空间转换都会刷新所有的TLB,就是因为没有这样的tag,这对虚拟化技术来说这是一个性能瓶颈。有一篇论文叫《Xen and the Art of Virtualiztion》,2.1.1节详细介绍了x86处理器存在的这个问题,解决方案是将每个地址空间顶部的64M划分给Xen,这样一来不会破坏x86的ABI,二来进出hypervisor也不会刷新TLB。对paravirtualiztion来说可以通过修改地址空间来减少地址空间的切换进而减少刷新TLB的次数。但是对HVM来说,是不允许修改guest OS的,为此Intel和AMD像SPARC那样引进了类似Context ID一样的标识以区分不同的地址空间。这样的话,从guest OS到hypervisor,或者不同的guest OS之间的切换都可以不刷新整个TLB。
参考文献:
Solaris Internals
Xen and the Art of Virtualiztion
The Definitive Guide to the Xen Hypervisor