提出问题
我们知道程序运行拿到的地址都是虚拟地址,和实际内存的物理地址是不一样的。
而我们程序产生的数据是要真正写到内存上去的。
这时候,就需要有个机制,帮我们访问的 虚拟地址,转换为 物理地址。
这个机制,或者说,硬件,就叫 MMU。
MMU 是怎么工作的呢?
原理
linux 采用了所谓的分页机制。
将我们的物理内存以 4KB 为一个页,分成非常多的 “页”。
而我们访问的内存地址,其实都是映射到这些页上去的。
下面就展示了一个典型的,从一个虚拟地址映射到物理地址的过程:
这里把虚拟地址分为了 3 段:
- 第一段[bit: 31- 20],其实是第一张 页表,的索引。
- 这个索引里,指向了第二级页表。
- 第二段[bit: 19 - 12],其实就是这个第二级页表的索引。
- 这个索引里,应该是存放了物理地址的高 20 位。
- 第三段[bit: 11 - 0],存放了物理内存的业内偏移。这个地址加上上面得到的物理地址高20位,加起来,就是真正的物理地址了。
大致,就是这个原理。
这里需要注意,使用多少级的页表、以及虚拟地址、物理地址的格式、PTE的格式等等,与32系统还是64位系统有关,也与处理器的架构有关,需要根据不同系统、不同处理器架构分析。
但是MMU工作原理都是一样的,不管是二级页表、三级页表还是四级页表,都是通过第一级页表找到第二级页表,通过第二级页表找到第三级页表…最终找到物理地址。只要明白了MMU的工作原理,分析其他的页表也是大同小异。
TLB
但是有个问题,就是,效率很低,上面这些页表,其实都是放在内存的,每次查询页表,都要访问内存,效率非常低。怎么解决呢?
这里就引入了 TLB,页面高速缓存。
比如,把一些最近用到的一些映射条目,放到CPU 的高速缓存中,每次在访问内存前,看看缓存是否可以命中,如果可以命中,就直接走 TLB 中的条目,就不用查内存了,提升了效率。
关于 TLB 的详细技术细节,以及 缓存失效等等,这里就不展开了。
缺页中断
在页表中,有个 present 标志位,表明这个页,当前是否在物理内存中,如果不在,就会触发一个 缺页异常,然后CPU 会从磁盘把这个 page 调入到 物理内存中。
相反地,当一些长时间不用的 页,长时间占用内存,内存管理模块会通过某种机制,将这些“占着茅坑不拉屎”的page,交换到磁盘,释放宝贵的内存空间。
缺页中断更多的技术细节,暂时也不展开了。