Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

TLB

复习

  • 页与分页:把虚拟内存和物理内存都切成固定大小的块
  • 页表:记录虚拟页与物理页帧的对应关系
  • 地址转换:沿着一个虚拟地址得到最终的物理地址

TL;DR

  • 每次访存都查页表,等于让内存访问次数翻倍
  • TLB 是页表的缓存,存储最近用过的“页号 → 页帧号”
  • TLB 命中,几拍就能拿到页帧号,不必查页表
  • TLB 未命中,才去查页表,并把结果填回 TLB

正文

  上一章结尾留下了性能问题:每访问一次内存,都要先查一次页表,而页表也在内存里。于是访存从一次变成两次,速度大打折扣。

  怎么救?用我们一路上最熟的老办法:加一层缓存

给页表加缓存

  程序访问内存有个特点:在一小段时间里,总是反复落在少数几页上(这正是讲过的时间局部性和空间局部性)。既然如此,把最近用过的“页号 → 页帧号”记在一个又快又小的地方,大部分查表就能省掉。

  这个小而快的地方,叫转译后备缓冲器(TLB,Translation Lookaside Buffer)。它通常就集成在 CPU 里,容量不大,但极快。

命中与缺失

  TLB 的工作逻辑和缓存一模一样:

  • 要转换地址时,先拿页号去 TLB 里找
  • 命中:几拍就拿到页帧号,直接拼出物理地址,完全不用碰页表
  • 缺失:只好去内存里慢慢查页表,拿到页帧号后,顺手把这一条记回 TLB,供下次使用
虚拟地址 → TLB? ──命中──> 页帧号 → 物理地址
             │
             └─缺失─> 查页表 → 页帧号 → 顺便写回 TLB

  因为局部性,绝大多数访问都会命中 TLB,那次“翻倍”的代价基本被摊掉了。

上下文切换时的小麻烦

  TLB 里存的是“某个进程的页号对应哪个页帧”。可每个进程都有自己的页表——同一个页号,在 A 进程和 B 进程里可能对应不同的页帧

  那么,进程切换时,TLB 里旧进程的记录就“过期”了,必须处理,否则就会用错映射。常见做法有两种:

  • 切换时清空 TLB(简单,但新进程要重新慢慢填充)
  • 给每条记录打上进程标记,只使用属于当前进程的那些(省事,但要额外比对标号)

  由此可见,上下文切换的开销,又多了一项“TLB 要重新预热”。这也是前面说“切换不便宜”的又一个原因。

思考题

  如果程序在一段时间里访问的页非常少、且反复访问,TLB 的命中率会怎样?反过来,如果它不停地在大量页之间乱跳,又会怎样?

小结

知识点

  • TLB 是页表的缓存,存放最近的页号到页帧号映射
  • 命中则跳过页表,缺失才查页表并回填
  • 局部性让 TLB 命中率很高
  • 进程切换时需清空或标记 TLB

参考资料

  1. Wikipedia(zh):转译后备缓冲器:TLB
  2. Wikipedia(zh):CPU缓存:缓存与局部性

思考题答案(仅供参考)

  如果程序在一段时间里反复访问少数几页,这些页的映射会一直留在 TLB 里,命中率极高,地址转换几乎不花额外时间——这正是局部性带来的好处。反过来,如果它在大量页之间乱跳,几乎没有复用,TLB 频繁缺失,每次都得去查页表,性能就会明显下降。这说明:TLB 也好,缓存也好,都建立在局部性之上;没有局部性的程序,再多缓存也帮不上忙。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪