物理内存的管理
Linux管理物理内存是使用分页机制实现的。为了使分页机制在32位和64位体系结构下高效工作,Linux采用了一个四级分页策略。
Linux支持多种内存分配机制。分配物理内存页框的主要机制是页面分配器,它使用了著名的伙伴算法作为物理内存分配机制。
管理一块内存的基本思想如下。刚开始,内存由一块连续的片段组成,下图的简单例子是64个页面。当一个内存请求到达时,首先向上舍入到2的幂,比如8个页面。然后整个内存被分割成两半,如图b所示。因为这些片段还是太大了,较低的片段被再次二分(c),然后再二分(d)。现在我们有一块大小合适的内存,因此把它分配给请求者,如图d所示。
现在假定8个页面的第二个请求到达了。这个请求有(e)直接满足了。此时4个页面的第三个请求到达了。最小可用的块被分割(f),然后其一半被分配(g)。接下来,8页面里的第二个块碑释放(h)。最后,8页面的另一个块也被释放。因为刚刚释放的两个邻接的8页面块来自同一个16页面块,它们合并起来得到一个16页面的块(i)。
虚拟地址空间
Linux为每个进程维持一个单独的虚拟地址空间。如下图所示。
虚拟地址空间被分割成同构连续页面对齐的区域。也就是说,每个区域由一系列连续的具有相同保护和分页属性的页面组成。代码段、数据段、堆、共享库段以及用户栈都是不同的区域。每个存在的虚拟页面都保存在某个区域中,而不属于某个区域的虚拟也是不存在的。在虚拟地址空间的区之间可以有空隙。所有对这些空隙的引用都会导致一个严重的页面故障。
在内核中,每个区是用vm_area_struct项来描述的。一个进程的所有vm_area_struct用一个链表连接在一起,并且按照虚拟地址排序以便可以找到所有的页面。当这个链表太长时(多于32项),就创建一个树来加速搜索。vm_area_struct项列出了该区的属性。这些属性包括:保护模式(如,只读或者可读可写),是否固定在内存中(不可换出)、朝向哪个方向生长(数据段向上,栈段向下)。
vm_area_struct也记录了该区是私有的还是跟一个或多个其他进程共享的。fork之后,Linux为子进程复制一份区链表,但是为了让父子进程指向相同的也表。区标记为可读可写,但是页面却被标记为只读。如果任何一个进程试图写页面,就会产生一个保护故障,此时内核发现该内存区域逻辑上是可写的,但是页面却不是,因此它把该页面的一个副本给当前进程同时标记为可读可写。这个机制就说明了写时复制是如何实现的。
vm_area_struct也记录了该区是否在磁盘上有备份存储,如果有,在什么地方。代码段把可执行二进制文件作为备份存储,内存映射文件把磁盘文件作为备份存储。其他区,如栈,直到它们不得不被换出,否则没有备份存储被分配。
一个顶层内存描述符mm_struct收集属于一个地址空间的所有虚拟内存区相关的信息,还有关于不同段(代码、数据、栈)和用户共享地址空间的信息等。一个地址空间的所有vm_area_struct元素可以通过内存描述符用两种方式访问。首先,它们是按照虚拟地址顺序组织在链表中的。这种方式的有用之处是:当所有的虚拟地址区需要被访问时,或者当内核查找分配一个指定大小的虚拟内存区域时。此外,vm_area_struct项目被组织成二叉“红黑”树。这种方法用于访问一个指定的虚拟内存地址。为了能够用这两种方法访问进程地址空间的元素,Linux为每个进程使用了更多的状态,但是却允许不同的内核操作来使用这些访问状态,这对进程而言更加高效。
下图强调了记录一个进程中虚拟存储器区域的内核数据结构。内核为系统中的每个进程维护一个单独的任务结构(源代码中的task_struct)。任务结构中的元素包含或者指向内核运行该进程所需要的所有信息(例如,PID,指向用户栈的指针、可执行目标文件的名字以及程序计数器)。
task_struct中的一个条目指向mm_struct,它描述了虚拟存储器的当前状态。我们感兴趣的两个字段是pdg和mmap,其中pdf指向第一级页表(页全局目录)的基址,而mmap指向一个vm_area_struct(区域结构链表),其中每个vm_area_strcuts都描述了当前虚拟地址空间的一个区域(area)。当内核运行这个进程时,它就将pdf存放在CR3控制寄存器中。
为了我们的目的,一个具体区域的区域结构包含下面的字段:
vm_struct:指向这个区域的起始处。
vm_end: 指向这个区域的结束处。
vm_prot: 描述这个区域内包含的所有页的读写许可权限。
vm_flags:描述这个区域内的页面是与其他进程共享的,还是这个进程私有的、
vm_next: 指向链表中下一个区域结构。
虚拟地中空间到物理地址空间的映射
虚拟存储的实现需要依靠硬件的支持,对于不同的CPU来说是不同的。但是几乎所有的硬件都采用一个MMU((Memmory Management Unit)的部件来进行映射,如下图所示。
在页映射模式下,CPU发出的是Virtual Address, 即我们的程序看到的是虚拟地址。经过MMU转换以后就变成了Physical Address。一般MMU都集成在CPU内部了,不会以独立的部件存在。