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