1. 虚拟和物理地址空间
每个进程的内存地址空间在大多数情况下要比系统中可用的物理内存要大。内核要考虑将实际内存划分给进程需要的地址空间,最可取的一个方法就是加一个页表。页表来为物理地址分配虚拟地址,进程的地址空间则用虚拟地址表示。如下图所示,不同进程的同一个虚拟地址可能会映射到同一个物理内存页(通常称为页帧)上,它们的含义是不同的。
2. 页表
页表的作用就是将虚拟地址空间映射到物理地址空间。那么最容易让人想到的办法就是使用一个数组,但在实际应用中最普遍的是多级分页。
2.1 数组页表
在虚拟地址空间的每一页都有一个数组项对应。那么如果虚拟地址空间为4GB,页表为4KB的话,就需要2^20项即1M项,如果每个项目是4B,那每个进程光页表就是4M,如果同时有100个进程在运行,那就消耗了400M内存。而实际上,很多页表项是标记为用不到的,所以这就造成了很大资源浪费。
2.2 多级页表
前面的数组页表,我们可以理解为一级页表,很多时候大家也都称之为线性寻址。 我们试想下用二级页表,能不能解决上面的问题。我们将原先的页表一分为二,页表项(10位)+偏移地址(10位)。还是上面的例子,不过我们这里的页表项可以索引到2^10*4KB大小物理地址,然后在通过偏移地址索引到具体的物理页帧。那我们计算下空间消耗,页表项2^10共1024项,如果有效页表项为64个,那么总的内存消耗就是1088*4B,约4KB。同理,在地址空间允许的范围内,分级越多,消耗的内存越小。但也并不是越多越好,毕竟逐级访问也是要消耗时间的。
一般来讲在IA-32的CPU上,使用两级页表就够了。但到64位体系结构上,由于地址空间比较大,就需要三级或四级的页表了。只要懂了上面二级页表的例子,多级页表的也很容易懂。如下图
3. 多级页表加速
多级页表可以节省大量空间,但缺点也显而易见,那就是要逐级多次访问数组才能拿到物理地址。CPU通过以下方法来加快这个寻址的过程。
- MMU(Memory Management Unit, 内存管理单元)
- TLB (Translation Lookaside Buffer, TLB) 地址转换后备缓冲器,这个缓冲器中保存地址转换最为频繁的地址。
注意这两个不是并列的,MMU是CPU中一种硬件,虚拟地址到物理地址空间的转换这部分工作正式它来做的,TLB则是MMU内部的一个小而且高速的存储器。当然MMU另一个重要的作用就是保护内存地址空间。
参考:
[1] https://baike.baidu.com/item/MMU/4542218?fr=aladdin
[2] 深入Linux内核架构