1. Introduction
VM系统遇到的挑战:
- 性能:5-20% runtime overheads, 在虚拟环境中可上升至20%-50%
- 新的硬件趋势:异构、NVM、3D
2. The Virtual Memory Abstraction
2.1 Typical VA Space
- 地址分布从低到高通常为:text, data, heap(向上生长), shared library, stack(向下生长),kernel
- 将kernel映射到地址空间中,可避免系统调用时进行进程切换(只需进行特权级切换)
- 在64位系统中,VA space通常用不完,“cannonical form”指地址的48-63位必须是一样的
- vDSO: virtual dyamically linked shared object, 提供一个用户能够访问的内核空间部分,避免syscall抵赖的保存现场开销。
2.2 Memory Permissions
- 一般地,访问权限不能同时为RWX。部分访问权限根据程序的object file中段的定义决定(.data/.text),还需要注意self-modifying code可能需要在运行时更改权限(RX->RW->RX)
2.3 Multithreaded Programs
- Thread是OS调度的单位,Process是资源分配的单位(state/address space)
2.4 Homonyms and Synonyms
- Homonyms: 一个VA映射到不同PA,需要ASID (Address space ID) 作为区分,否则需要在进程切换(地址空间切换)时flush TLB。注意:对于VIVT的cache或store buffer, 由于VA作为tag也会面临同样的问题。
- Synonyms: 不同VA(可属于同一个进程或不同进程)映射到同一个PA,即同一个physical page有可能对应多个PTE. 主要影响包括:
- reverse-mapping: 某一个PTE在page内容被修改时将其标记为dirty, 但其他指向同一个物理地址的PTE也需要被修改。同理,在将页换出时需要将所有PTE标记为不在内存中。这需要reverse-mapping,通过物理地址找到所有PTE。
- cache structure: VIPT cache需尽量避免不同set内存在同一个physical address block.
- store buffer: 即使有ASID+VA,incoming load仍有可能VA不同,PA相同。解决方法可能是使用PA作为标签,但TLB会成为进入store buffer的关键路径。另一种解决方法是在store buffer中投机(assume no synonym checking is needed for an incoming load)。(这种方法是否本质上仍需记录并检测PA matching?)
2.6 Virtual Memory Management
- User-level memory management的作用:C/C++ malloc() 或new()会首先调用C library, 优先在memory pool中分配,若不够再调用操作系统的mmap/brk.
3. Implementing VM: an Overview
Address translation优化原则:common case用硬件实现,其余情况依赖OS
3.2 Page table basics
- bit63&bit1: R/W/X permission
- bit2: user/supervisor
- bit8: global (进程切换时无需更改)
- bit7,4,3: cacheability
- bit6&5: dirty and accessed
3.3 TLBs
- Skylake: 64entry L1 TLB, 1536 L2 TLB
- Cortex A73: 48entry L1, 1024 L2
- TLB miss可由软件(OS)或硬件(Hardware page table walker)处理
3.4 Page and segmentation faults
- Minor page fault: 页存在于物理内存中,但PTE不存在或权限未被设置(可能出现在lazy allocaition、copy-on-write等情况)
- Major page fault: 访问的页不在物理内存中
- Segmentation fault: 非法访问(VA未分配/读写权限错误)
4. Modern VM Hardware Stack
4.1 Inverted Page Table
- 主要解决页表的三个问题:1)占据空间大;2)multi-level radix tree需要多次访存;3)维护代价(每个线程需有单独的页表)
- Inverted page table为每个物理页维护一个表项,用VA与反页表项对比,match后得到物理地址。可采用对VA做hash来减小搜索代价,但需要额外的hash collision机制:
- 在IPT内维护next pointer, 缺点是可能增加访存次数;
- 每次新增表项时增大IPT的大小
- Hash anchor table (HAT): 先用hashed VA索引HAT,再索引IPT。该方法的优点在于可以增大HAT以减小collision chain长度。
4.2 TLB Arrangement
4.2.1 Multi-level TLBs
- Unified/split: L1 TLB通常分开指令和存储,避免contention. 而次级TLB可共享。
- Shared/private: 最后一级TLB可以共享(容量大)或私有(靠近核速度快)
4.2.2 TLB placement relative to CACHE
- PIPT: TLB访问在关键路径上。但优点是cache管理简单(coherence request使用PA,同时无需考虑不同进程重名的问题)
- VIVT: TLB在L1 cache miss之后才会被访问,主要优点在于L1 hit无需访问TLB。但会引起以下问题:
- 访问权限、合法性检查:仍需要在指令retire前检查合法性。
- Synonym: 不同VA映射为同一PA,导致同一个data block出现在不同cache set中。需要额外的机制避免。
- 多进程:需要额外的ASID支持,增大Cache的存储容量。
- VIPT: 只能用“无需translation”的地址部分作为索引(即page offset)。其主要缺点为只有12bit(page offset)- 6bit(cache block)可作为set索引,最多支持64个set. 因此L1 cache通常只有8way64set64B=32KB.
- 若使用部分VPN进行set索引,则可能导致同一个PA映射到两个不同cache set上。需要使用page coloring解决。
4.3 TLB Replacement Policies
TLB替换策略与cache替换和Page替换策略略有不同,通常L1 TLB时序较紧张,无法实现复杂的LRU,可用随机替换代替。L2 TLB可实现更复杂的替换机制。
4.4 Multiple page sizes
若TLB不是fully-associative, 支持多种页大小存在一定难度:TLB set的索引需要知道页的大小,而页的大小信息存放于PTE内。解决方法有以下两种:
- Split TLBs: 同时检索两个不同页大小的TLB,若有一个hit则hit,全部miss则访问下一级TLB。
- Section 8.2
4.5 PTE Metadata
- Permission information: 包括当前特权级和该页的读写执行权限
- Accessed bit: page table walker将PTE中的accessed bit置位,OS(取决于页管理机制)可能定期将其复位。注意TLB一般不参与accessed bit的管理,因为在TLB中的PTE均为最近要访问的。
- Dirty bit: 需要TLB参与。在写操作时,若TLB miss或TLB hit但其dirty bit为0,page table walker都需要更新PTE的dirty bit. 若TLB中的dirty bit已为1,则无需进行PTE的更新。
- ASID: 若TLB中不添加ASID支持,在地址空间切换时则需要FLUSH,通常需要5-20us时间(对于每秒1000次进程切换就是5-20ms)。实现方法为在TLB tag match时与控制寄存器中存放的ASID相比较。
- ASID与PID的区别:ASID位宽更小(eg: 12bit),PID通常为32bit. OS需要额外地维护ASID与PID之间的映射。
- Global bit: 用于避免kernel address space在进程切换时冲刷TLB,即global bit置位时无需进行ASID比较。
4.6 Page Table Walkers
4.6.1 SW-Managed TLBs
TLB miss触发异常,OS用一条特殊指令填充TLB。但每次TLB miss需要切换到OS,代价较大。且软件需要使用VA来访问页表,需要额外的映射。
4.6.2 HW-Managed TLBs
- 主要优点为:无需触发中断、冲刷流水线、cache和predictor pollution, Page table walk时间可以隐藏在OoO执行之下(只要能找到能issue的指令)
- x86: CR3指向page directory,ARM: TTBR0指向per-process page directory, TTBR1指向kernel structures.
- 可以集成多个page table walker以实现TLB miss的并行处理。
4.6.3 MMU Caches
TLB覆盖范围有限,为了加速TLB miss, CPU vendors使用MMU cache存储L2-L4的Page directory entry. Page table walker在访问每级page directory时先在MMU cache中查找。
- Intel Paging Structure Caches (PSC): 用VA作为MMU CACHE的tag, 同时查找L4/L3/L2,从最长match者内的PPN出发进行page table walk.
- AMD Page Walk Cache (PWC): 用PIPT cache作为MMU cache. 从CR3+L4 offset开始逐级查找,若命中则可以跳过该级访存。
4.6.4 Translation Storage Buffers
用于SPARC,基于软件的TLB miss管理方案,在TLB miss时通过trap首先查找TSB,若没有命中才进行Page table walk.
Sidebar: 发生缺页时的具体过程? 1. PTE INVALID,指示page fault,进程陷入内核态 1. 检查要访问的虚拟地址是否合法 1. 分配物理页(lazy allocation),可能需要读取磁盘 1. 建立VA到PA的映射关系
5. Modern VM Software Stack
5.1 Virtual Memory Management
OS需要管理每个进程对应的VM地址空间,在每个page faultt和mapping操作时需要管理VMA。在每个进程的mm_struct中存在指向VMA的指针。
5.1.1 Demand paging and lazy allocation
- Simple approach: 在新的mapping时分配VMA,同时分配物理内存并更新页表。
- Lazy allocation: 分配VMA时不分配物理内存,程序访问新虚拟页时再分配。
- 程序通过malloc()分配内存时,实际是OS通过brk和mmap分配。其中前者生长heap, 后者在堆和栈之间的虚拟地址空间中分配空闲虚拟地址。两者实际更改的都是VMA。
- 程序访问该VM时遇到page fault (由于translation不存在), 可能是minor page faults也可能是file-backed major page faults.
- OS分配物理内存,创建页表项
5.1.2 Copy-on-write
Process fork后,OS将两个进程的相应PTE标记为不可写,但将它们的VMA标记为可写。从而在这样的protection fault发生时能够识别出copy-on-write.
5.1.3 Address space layout randomization (ASLR)
5.2 Managing Locality
需解决三个问题:1)监测最近访问的页面;2)逐出哪些页面;3)从磁盘读入哪些页面。
5.2.1 Working Sets
Working set W(t,a)代表进程在(t-a,t)内访问过的页面。
5.2.2/3 Page Replacement Policies
Page replacement policy的核心是如何有效利用PTE中的accessed bit信息。
- Optimal: 逐出未来最长时间不会被使用的页 (belady's algorithm)
- LRU: 理想的LRU需要记录每个页的timestamp或counter, 在逐出页面时还需要检查全部页的counter信息,从而找到对应的页。
- Approximate LRU: CLOCK algorithm 利用PTE中的accessed bit
- 通常优先通过CLOCK算法evict clean page, 若没有再找dirty page.
5.2.4 Page Buffering
当预测到dirty page会被逐出时,提前将其写回磁盘,从而避免逐出操作出现在关键路径上。
- page buffering: 维护空闲物理内存池,当空闲内存数量过少时提前写回/逐出页面。若该页面再次被访问,则无需从磁盘中获取;若否,则该物理页面被替代为其他页,无需再次写回至磁盘。
5.3 Physical Memory Allocation
OS需要维护空闲物理地址记录,减少external/internal fragmentation
5.3.1 Naive Memory Allocators
Best fit/worst fit/first fit
5.3.2 Buddy Allocation
维护0-K个空闲列表,分别记录2^0 - 2^K的空闲款位置。
- 分配/释放速度快:直接查找待分配size的位置
- 外部碎片较少
5.3.3 Memory Pools and SLAB Allocation
- buddy algorithm的主要问题是必须分配power-of-2的存储空间,造成内部碎片。可通过使用user-level allocator+memory pool避免这个问题。
- SLAB allocator: 用于kernel分配某些固定大小的数据结构(inode, task control block等等)
5.3.4 Page Coloring
Memory allocator通过coloring将working set均匀分布在cache set中。
5.3.5 Reverse Mapping
当page swap out时, 需知道该物理页面对应的PTE(可能属于不同进程),因此需要维护reverse mapping. 一般使用优先搜索树实现。
6. Virtual Memory, Coherence and Consistency
多数处理器的coherence机制只针对data cache, 没有对TLB coherence的处理(指令cache也没有)。
6.1 Non-coherent caches and TLBs
- TLB和ICache多数时间被读,因此实现硬件coherence的代价较大
- TLB用VA索引PA作tag, 每次snoop需要搜索所有set.
6.2 TLB shootdown
TLB shootdown的实现方法包括:专用的指令/side effect of accessing a CSR, shootdown的粒度也有多种。
6.2.1 Invalidation Granularity
- 通常提供shootdown某个TLB项的指令。例如:ARMv8中的tlbi VA
- Context switch时通常需要invalidate整个TLB(除global外,且假设操作系统或硬件不支持ASID),因此也有此类指令支持。例如:ARMv8中的tlbi ALL,x86中通过写CR3实现TLB冲刷
- Linux中存在一个阈值来选择逐页/全局invalidation, 可使用启发式算法来更新。
6.2.2 Inter-processor interrupts
ARMv8 tlbi对所有核作用;Power架构同时提供local/global两种tlbi指令;X86通过IPI实现。 特别地,对于X86 IPI而言,每次发生上下文切换时均通过IPI冲刷所有核的TLB显然代价过大,可通过“cores working in the same context”来完成区分。
6.2.3 Optimizing TLB shootdowns
- 对于dirty bit,在发生置位时(0->1)无需shootdown其他核的TLB,最坏后果只是其他核在被写入后可能仍需更新一次PTE(TLB hit但dirty bit=0, 需要一次page table walk)。但这个代价可能小于shootdown. 反之,在dirty bit由1到0更新时则需要shootdown. 若TLB dirty bit为1,而PTE dirty bit为0(页已写回磁盘),则OS可能会对此后的写操作不知情(PTE不会被更新为dirty),导致功能错误。
- 虽然TLB中一般不存放accessed信息,但OS在清除accessed bit之后(如使用CLOCK algorithm), TLB若不进行shootdown, 则下次访问时可能无需page table walk, 导致该项对应的accessed bit无法被正确地置位,最终使得profiling失效。反之,在OS将accessed bit置位后无需管理TLB。
- 综上,多数操作系统在dirty/accessed bit清零时需要进行TLB shootdown. 同理,在某个页面的权限发生升级时也需要shootdown.
6.2.4 Other details
- 当TLB shootdown由物理内存改变引起时(例如page swapped out),可能需要经过reverse mapping,查找到所有的TLB entry.
- 还需要注意page table walk cache (MMU CACHE) 的invalidation.
6.3 Self-modifying code
- 使用场景:动态链接(Procedure linkage table),JIT compiler,runtime optimization, profiling等,在没有ICache HW coherence的情况下需要保证执行正确的指令。
- X86使用硬件处理:直接跳转至修改后的代码即可。(但在多线程/VA synonym时仍需要同步)
- ARM/POWER需要OS显示地保证对指令的修改已写入存储并同步:例如ARM需要DC CVAU (clean by VA to PoU); DSB ISH; IC IVAU; DSB ISH; ISB
6.4 Memory Consistency Models
TLB shootdown/page table walk等行为在memory看来与普通load无异,但却不受普通memory fence的限制。因此在更新PTE时需注意用更强的barrier指令限制page table walker的行为,否则可能引起错误。
为弥补传统memory model (只限制普通load/store)和VM子系统中其他部分之间的gap, 研究人员提出:
- Memory Persistent Model (for NVM) by Pelley
- Consistensy model for filesystem by Bornholt
- Transistency model (take PTE status bits into accounts)
7. Heterogeneity and Virtualization
7.1 Accelerator and Shared Virtual Memory
- Cache/TLB/page tables在不同的设备上,如何保证coherence? AMD infinity fabirc/Power+NVIDIA Volta
- OS的支持:HMM (heterogeneous memory management) patchset for linux. 将CPU进程的地址空间镜像到设备上。
- GPU
7.2 Memory Heterogeneity
7.2.1 NUMA
- NUMA可能源于存储的位置远近,也有可能源于物理介质的不同。
- 早期的分布式NUMA无cache coherence, 导致编程非常困难。
- 为实现跨socket通讯,AMD提出HyperTransport, Intel QPI 谁应该来处理因位置、介质不同带来的访存速度差异? OS/compiler/programmer? 看作不同cache层次还是不同存储位置?
7.2.2 Emerging memory technologies
- 传统FLASH与DRAM的速度/带宽差距过大;而NVM弥补了这个差距给设计空间又添加了另一个维度:persistency. (外加读写的不对称性)
- ACPI对memory zone的区分维度:latency/bandwidth/persistence/cacheability
7.3 Cross-device communication
7.3.1 Direct Memory Access (DMA)
7.3.2 IOMMU
当外设访问CPU存储时,若设备的TLB发生miss, 则需要IOMMU来处理。注意IOMMU对应的页表是被host CPU管理的。 例:当GPU TLB miss时,通过PCIE的Address Translation Service发送地址转换请求到IOMMU,IOMMU根据PASID找到页表的基地址。若IOMMU page table walk发生page fault,则GPU重新发出Peripheral Page Request (PPR)请求,需要OS介入进行分配。
7.3.3 MMIO
将外设映射到物理地址空间,用于方便CPU读写外设。例如:一个PCIe设备将一些寄存器通过PCIe设置暴露出来,bootloader在启动时会发现这些寄存器,驱动程序完成这些寄存器的mapping.
- 需保证IO mapped读写会到达设备,可通过标记“non-cacheable”实现
- 与正常的读写语义有差别,例如读可能有side-effect, 读出的值不一定是最近写的
7.3.4 Non-cacheable/coalescing accesses
存储系统对正常语义的读写可以有reorder/buffer/coalesce等动作,以提升吞吐量,降低延迟。但对于MMIO操作需要特殊处理。
- X86:Page Attribute Table (PAT), 将存储区域标记为uncacheable,保证其不被cache、buffer或reorder.
7.4 Virtualization
虚拟化需要两级页表转换:per-process gPT和per-VM hPT
7.4.1 Nested Page Table
guest CR3 (gPA)首先经过nested page table (5次访存), 得到guest L4 page directory项;以此类推共24次访存后得到真正的物理地址。为加速这个过程,CPU使用以下三种机制:
- Private per-CPU TLB: 在TLB hit的情形下,直接得到gVA->hPA的转换。但若TLB miss, 则需要进行nested page table walk.
- Private per-CPU MMU cache: 利用MMU cache, 不必从CR3找起;
- Private per-CPU nTLBs: 该TLB存放gPA->hPA的转换,可以跳过nested page table的逐级访问。
7.4.2 Shadow Page Table
Hypervisor维护shadow page table, 内含gVA->hPA转换。在TLB miss时无需进行nested lookup. 但该方法的主要缺点是更新页表的代价较大。(guest OS更新页表需要hypervisor更新gVA->hPA的转换关系。
8. Advanced VM Hardware
用于加速地址转换的硬件方法无需改变软件栈,因此可能得到更快的应用。
8.1 Improving TLB Reach
8.1.1 Shared Last-level TLB
一般CPU core使用私有的L1/L2 TLB,通过Shared L2 TLB,可在各core共享某些地址转换时减少miss, 从而减少page table walk.
- TLB entry design: 与普通TLB entry一样
- Multi-level inclusion: no back-invalidation overheads (exclusive) vs. coherence message filtering (inclusive)
- Mostly-inclusion: 发生miss时同时更新L1/L2,但L2在发生替换时不必反向invalidate L1。
- Exclusive: L2只在L1 eviction时填充(类似victim cache),且利用率更高。
- Inclusion: 需要back-invalidation,但能够filter coherence message.
- Integrating with prefetcher: 可预取当前正在取的页的下一个页。注意,预取不应造成新的page table walk. 可通过“访存位宽>PTE长度”实现预取。
8.1.2 Part-of-memory TLBs
将DRAM分成两部分,每次访问DRAM恰好能够包含多个TLB entry.
8.1.3 TLB Coalescing
在TLB中实现“连续的几个虚页->连续的物理页”之间的映射。
- Source of intermediate contiguity: Superpage提供的是较长的连续性(2MB pages),而coalescing TLB提供“适中”的连续性(8-16)。
- memory allocator保证虚拟页是连续的;
- memory defragmentation engine保证有连续的物理页面可分配;
- 最终应用程序访问虚拟页时发生minor page fault,完成映射
- Coalesced TLB design options:
- TLB lookup: 由于coalsced entry需要出现在同一个TLB项中,因此需要用VPN的中间某些位索引set, 高位做tag比较,低位与TLB entry中的bitmap相比较,检查该entry是否真的含有被查找的地址转换。最终,Coalesced TLB entry中的PPN需要添加因coalescing造成的偏移后,组成真正的PPN。
- TLB fill: page table walker取到连续的8个entry时,coalsced TLB添加组合逻辑检测这些项能否被合并。
8.2 HW support for multiple page sizes
Split sized TLB虽支持多种页大小,但主要缺点是浪费面积。下列方法以增长访问时间的代价同时支持多种页大小,多用于次级TLB。
8.2.1 Multi-indexing approaches
- Fully assocaitve: 每个entry内存放一个mask与VPN相与后再比较;
- Hash-rehash: 通过hash索引set, 首先进行原始4kb页的查找,若失败则再尝试另一种页大小。但该方法使TLB查找用时是变化的。
- Skewing: 不同way用不同index function. 没看懂
8.2.2 Using prediction to enhance multiple indices
问题的根源在于无法提前知道页大小,导致需要多次查找。可通过预测使hash-rehash免于多次查找。
- PC-based page size predictor: 访存指令的PC在访问TLB之前就被知道,所以能够留出足够的时间实现预测。缺点是多个PHT entry学习的其实是同一个页面的size.
- Register-based predictor: 根据观察,VA大部分由src1 register确定,因此只需要用src1的置索引PHT即可。
8.2.3 Using coalesced approaches
只用普通页大小的索引方法,但在遇到大页时将其复制到所有set中。
8.3 TLB Speculation
IDEA: VA和PA通常存在PATTERN
8.4 Translation-triggered Prefetching
TLB miss后CPU的load大概率会遇到cache miss, 因此可在page table walk时进行LLC prefetch.
- 如何得到待预取的PA:在memory controller中暂存cache line number, 取到PA后马上进行prefetch
- 如何保证预取的时效性:节省一些TLB FILL的时间?
8.5 Other HW Improvements
translation coherence (interaction with virtualization/HBM/heterogenity)
- UNified instruction/translation/data (UNITD) coherence: One protocol to rule them all.
- DiDi: Mitigating the performance impact of TLB shootdowns using a shared TLB directory.
- Hardware translation coherence for virtualized systems.
9. Advanced VM HW/SW Co-design
9.0 Purely SW topics
- OS support for transparent superpages: 传统支持大页的方法为编程者链接libhugetlbfs, 必须提前知道所需的页数。最近的OS中添加transparent superpage支持。
- OS support for efficient TLB coherence:
9.1 Recency-based TLB preloading
常见的预取算法包括stride prefetching/multi-core prefetching/recency-based
- Basic Idea: 在translation的recency stack中,下次访问很可能与本次访问处在同样的栈深度中。
9.2 Non-contiguous superpages
物理地址对应的DRAM若有缺陷将会导致superpage全部无法分配。 Gap-tolerant Sequential mapping: 将大页分为一些小的building-blocks, 并保证这些building block在物理内存中的分布不超过页大小的2倍。
9.3 Direct Segments
对“big memory workload”(例如database/graph algorithms等)而言,不需要swapping/fine-grained protection等功能,对存储的使用偏向于“静态”,因此可使用segment机制。
- 无需swapping: 基于页的VM的优点在于无需用户参与就能自动地将页面换入换出。而对于大内存/performance-critical的应用不需要/不能接受swapping带来的代价。
- 无需allocation&fragmentation: 例如MySQL启动时一次性分配好存储空间
- 无需per-page permission:
9.4 Other HW/SW Approaches
- Direct segments in virtualization
来自Morgan claypool的synthesis lectures on computer architecture系列丛书