Linux支持多种硬件体系结构,因此Linux必须采用通用的方法来描述内存,以方便对内存进行管理。为此,Linux有了内存节点、分区、页框的概念。
内存节点:主要依据CPU访问代价的不同而划分。多CPU下环境下,本地内存和远端内存就是不同的节点。即使在单CPU环境下,访问所有内存的代价都是一样的,Linux内核依然存在内存节点的概念,只不过只有一个内存节点而已。内核以struct pg_data_t来描述内存节点。
内存分区(ZONE):Linux对内存节点再进行划分,分为不同的分区。内核以struct zone来描述内存分区。通常一个节点分为DMA、Normal和High Memory内存区。
DMA内存区:即直接内存访问分区,通常为物理内存的起始16M。主要是供一些外设使用,外设和内存直接访问数据访问,而无需系统CPU的参与。
Normal内存区:从16M到896M内存区。
HighMemory内存区:896M以后的内存区。
896M是个分界点,低于这个界限的称为low memory,高于这个界限的称为high memory。low memory包含了ZONG_NORMAL+ZONE_DMA。
32位Linux虚拟内存空间为0-4G,其中0-3G用于用户态,3G-4G用于内核态。其中相当于内核态又分为3G-3G+896M 和3G+896M-4G 这两个部分,其中最后的128M划分到了high memory,为什么要这样设计?原因是低内存区页表的映射关系是固定的,系统初始化时就建立了,内核空间本来1G就小,还不灵活,当然不行,所以留出128M来做两件事:1.这部分空间可以通过页表操作映射到高端物理地址,2.连续的虚拟地址空间可以映射到非连续的物理内存。这在没有大段连续的空闲物理地址时,是非常重要的。
x86-32上分区总结如下:
页框:操作系统内存管理方式有:段式、页式、段页式。Linux采用页式内存管理,页是物理内存管理的基本单位,每个内存分区又由大量的页框组成。内核以struct page来描述页框。页框有很多属性,这些属性描述了这个页框的状态、用途等,例如是否被分配。
三者之间的关系如下图所示:
介绍完节点、分区和页框关系后,我们接着再看看页、页表、页框的关系:
我们知道,CPU并不是直接访问物理内存地址,而是通过虚拟地址空间来间接的访问物理内存地址。操作系统为每一个正在执行的进程分配的一个虚拟地址空间(逻辑地址),在32位机上,其范围从0 ~ 4G-1。操作系统通过将虚拟地址空间和物理内存地址之间建立映射关系,让CPU间接的访问物理内存地址。
页:将进程分配的虚拟地址空间划分的块,对应的大小就叫页面大小。
页框:将内存物理地址划分的块。
页和页框二者一一对应,一个页放入一个页框,(理论上)页的大小和页框的大小相等。
页表:操作系统通过维护一张表,这张表上记录了每一对页和框的映射关系,这个表即页表。页表被放在物理内存中,由操作系统维护。
三者关系关系如下图所示:
两个小例子来捋下他们之间的关系:
一、计算页表占用的内存大小:
已知条件:逻辑地址32位、页面大小4KB、页表项大小4B,按字节编址。
首先 32 位的虚拟地址可表示的进程大小应该是2^32B = 4GB(暂时别去想页号P占多少位,W占多少位)。根据页的定义和页面大小的定义将进程进行分页:
页面的数目为:2^20页, 所以页表就需要4B*2^20 = 4MB的空间存储。
二、物理地址访问流程分析
首先通过计算得到CPU访问的虚拟地址,虚拟地址是由页表号+业内偏移地址组成,把这个地址传给页表寄存器即MMU,它对应到物理地址的页框号,访问物理地址找到框的起始地址,然后加上偏移,访问最终物理地址。
注意,每个进程都有页表,页表起始地址和页表长度的信息在进程不被CPU执行的时候,存放在其PCB内。
按照上述的过程,可以发现,CPU对内存的一次访问动作需要访问两次物理内存才能达到目的。
为了提升效率,为了提高CPU对内存的访问效率,在CPU第一次访问内存之前,加了一个快速缓冲区寄存器TLB(Translation Lookaside Buffer),TLB是MMU的核心部件,它缓存少量的虚拟地址与物理地址的转换关系,是转换表的Cache,俗称“快表”。当TLB中没有缓冲对应的地址转换关系时,需要通过对内存中转换表(大多数处理器的转换表为多级页表)的访问来获得虚拟地址和物理地址的对应关系,引出MMU的另一核心部件TTW(TranslationTable walk)。TTW成功后,结果应写入TLB中。TLB里面存放了近期访问过的页表项。当CPU发起一次访问时,先到TLB中查询是否存在对应的页表项,如果有就直接返回了。整个过程只需要访问一次内存。如图:
这种方式极大的提高了CPU对内存的访问效率。然而这样的方式还是存在弊端,在物理内存中需要拿出至少1M的连续的内存空间来存放页表。注意关键字不是1M,而是连续的。
解决办法是可以通过多级页表的方式,将页表分为多个部分,分别存放,这样就不要求连续的整段内存,只需要多个连续的小段内存即可。
参考:
https://www.cnblogs.com/youngerchina/p/5624516.html
https://blog.csdn.net/displayMessage/article/details/80905810
http://www.it610.com/article/3744890.htm