首先,我们要知道一个概念:MMU(内存管理单元),MMU可以通过CPU将线性地址转换成物理地址。我们再来看Linux的内存管理,Linux内存管理机制分为页式管理和段式管理两种管理机制,对于段式内存管理机制,是将指令中结合段寄存器使用的32位逻辑地址映射为32位的物理地址。
随着操作系统的发展,提出了保护模式和虚拟内存管理的概念, Intel在分段机制的基础上实现了包含模式和虚拟内存管理,后来出现了分页机制并逐渐成为内存管理机制的主流。Intel出于兼容性的考虑,保留了段式内存管理机制,段寄存器用做选择子,段基地址及其他的某些属性存放在内存中,称为段描述符表。
段式内存管理机制的灵活性和效率都比较差,这样就需要采用页式管理机制。页式管理就是通过分页单元将线性地址转换为物理地址,其中一个重要的任务就是把请求的访问类型与线性地址的访问权限进行对比,如果该内存访问无效,则产生一个缺页的异常。
内核把线性地址分为4KB大小的页面,其中的每个页面都可以映射到物理地址空间的任意4KB大小的地址。在该映射过程中,连续的线性地址映射到物理地址空间中不一定是连续的,分页单元把所有的RAM分成固定长度的物理页,每一个物理页( Page Frame)包含一个页。
把线性地址映射到物理地址的数据结构称为页表,该页表存放在主存中,并在启用分页单元之前由内核对页表进行初始化。由于引入了页式管理,因此32位的线性地址分为3个区域,分别为 Directory(目录)、Table(页表)及0 Offset(偏移)。目录是线性地址的最高10位,用做页面表目录的下标,指向一个页面表;页表是线性地址的中间10位,用做具体页面表中的下标,指向具体的物理页面;偏移则是线性地址的最低12位,用于指向具体物理页面内的偏移量。对于每一个活动进程都有一个分配的页目录,正在使用的页目录的物理地址存放在cr3控制寄存器中。线性地址的 Directory字段決定了页目录中的目录项,目录项指向某个页表,线性地址的 Tab le字段则決定了页表中的页表项,0 cfset字段決定页框内的偏移量,由于该字段包含了12位,因此每页包含了4096字节的数据。
线性地址到物理地址的映射,先从cr3寄存器中获得页面目录的基地址,然后以 Directory字段为下标,在目录中取得相应的页面表的基地址,之后以线性地址中的Page字段为下标,取得相应的页面描述项,最后将页面描述项中给出的页面基地址与线性地址中的0ffset字段相加得到具体的物理地址。
由于大型的服务器需要大于4GB的RAM来同时运行大量的进程,因此必须扩展32位的X86结构所支持的RAM,Inte通过把处理器引脚数从32扩展到36满足了这些需求。从 Pent ium Pro开始,Inte对物理地址的宽度进行了扩展,即物理地址扩展(PAE)机制,这样可以把32位的线性地址转换为36位的物理地址,该机制中通过设置cr4控制寄存器中的物理地址扩展标志来激活PAE。为了支持PAE也改変了分页机制,64B的RAM分为2的24次方个物理页,页表项的物理地址字段从20位扩展到24位,同时引入了页目录指针表的页表新级别,由4个64位表项构成。cr3寄存器包含了27位的页目录指针表基地址字段,当把线性地址映射到4KB的页时,32位线性地址中的0~11位为4KB页中的偏移量,12~20位指向页表512项中的一个,2139位指向页目录512项中的一个,30~31位指向页目录指针表4项中的一项,对于cr3则指向一个目录指针表。
Linux内存管理机制看起来很复杂,实际上只需要集合Linux的内核和系统结构,加以分析,理解起来事半功倍。
代码小兵86504-19 19:55
代码小兵49806-21 15:40
代码小兵69607-21 11:32
代码小兵87207-21 12:51