本文已参与「新人创作礼」活动,一起开启掘金创作之路。
理解内存
程序并不能直接访问物理内存,它使用的都是虚拟内存地址。只有通过虚拟内存地址到物理内存地址的地址转换,才能到达实际存放数据的物理地址。
简单页表
我们的内存(不管虚拟还是物理)都是被分成了固定大小的页(页的大小一般为4KB),而且同一个页里的内存地址,在物理层面是连续的。要想实现虚拟内存地址到物理内存地址的转换,我们可以建一张映射表。这个映射表,可以将虚拟内存地址到物理内存地址一一映射。我们把它叫做页表(Page Table)。
这个地址映射的办法,就是把虚拟内存地址分成页号和偏移量。通过这个页号来确定这个地址在哪一个物理页中,再通过偏移量来确定物理页中的具体位置。
如果我们直接采用这种方法,假如内存是32位的,页的大小是4KB,那么我们需要2^20(2^32 * 8)/(4 * 2^10 * 8)个映射关系,每个映射大小4字节。那么我们一个页表就需要4MB的大小的内存空间。而我们的进程都有属于自己独立的虚拟内存地址空间。也就是说,每一个进程都需要这样大的一个页表,这样的话我们的内存就不够用了。我们还需要优化它。
多级页表
我们知道大部分进程所占用的内存是有限的,也就是说需要的页是有限的。那我们就不用存下这所有的2^20个映射关系了,只需要存下用到的映射关系就行。而且在一个实际的程序进程里面,虚拟内存占用的地址空间,通常是连续的。我们来看看多级页表是如何优化的。
-
每个进程都有一个四级页表。我们通过四级索引来找到四级页表中的一个条目,这个条目对应的是众多三级页表位置中的一个。
-
找到三级页表的位置之后,通过三级索引在三级页表中找对应2级页表位置的条目,同样这个条目也是众多2级页表位置中的一个。
-
同样的方式,在二级页表里通过二级索引找到一级页表的位置。
-
找到一级页表的位置之后,通过一级索引来找到对应物理页的一级条目。最后结合偏移量就找到了最终的物理地址。
我们可能需要很多的一级页表,二级页表,甚至三级页表(1>2>3)。但是因为实际的虚拟内存通常是连续的,所以我们也可能只需要很少的二级页表,甚至只需要一张三级页表。
如果就按分成四级多页表,每级索引用5个比特。那么一级页表就会有2^5=32个条目,如果每个条目还是按4字节来看,那么就一共128字节。而一个一级索引页表,对应32个4K比特,也就是32*4K比特=16(128/8)K字节大小的物理内存。一个满的二级页表,对应32个一级页表,也就是512KB大小的物理内存。