2.1.3 页式寻址
前面的内容向读者阐述了段的演进历史,以及其在内核中的作用。因为内核使用了平坦内存模型,将段基址设置为0,逻辑地址就是线性地址了,内核基本屏蔽了段机制,接下来,我们将放下段这个包袱,只关注页式内存管理。
为了使用页式内存管理,操作系统将物理内存划分为多个页面,然后通过一种机制,将虚拟地址映射到具体的物理页面内的偏移。以32位地址为例,如果页面大小为4KB,那么需要12位寻址页内偏移地址,假设我们使用1级页表映射,那么余下的20位用作页表内表项的索引,因此页表内将有220个表项。假设每个页表项占据4字节,那么页表的尺寸是220×4bytes=4MB,如果系统中运行多个任务,而每个任务又都有自己的页表,那么多个页表占据的内存就是一笔不菲的开销。更重要的是,页表中的大部分表项可能从来不会用到,白白浪费内存。
如果我们将1级表拓展为2级表,1级表中的表项指向的不是最终的物理页面,而是2级的页表,2级页表中的表项指向的是最终的物理页面。刨除12位用于4KB页面页内寻址,我们将余下的20位划分为2个10位,第1个10位用作1级表的索引,第2个10位用作2级表的索引。假设每个表项占据4字节,那么1级表的大小为210×4bytes=4KB。同理,2级表的大小也是占据4KB。但是,2级表是按需分配的,所以2级表占据的尺寸就变为N×4KB,N为实际需要的2级表数量。相比于使用1级页表映射,其所占据的内存空间少了很多,相当于“实报实销”了。那么是否可以有多张1级表呢?答案是不可以,因为cr3寄存器只能记录一张表的地址,所以整个表需要有一个根,即1级表只能有一个。典型的2级页表映射如图2-4所示。
图2-4 2级页表下线性地址到物理地址的映射
对于32位CPU而言,使用2级表是一个比较好的平衡,但是对于64位CPU,如果依然使用2级表,与32位系统使用1级表类似,同样面临着单个表尺寸过大,导致物理内存浪费的问题。于是,64位系统使用4级或5级表。所以,操作系统最终是使用1级、2级还是3级表,甚至更多级表,是从表占用内存的经济角度考量的。