Linux 核心--4.内存管理( 六 )



当页面块被释放时,代码将检查是否有相同大小的相邻或者buddy内存块存在 。如果有,则将它们结合起来形成一个大小为原来两倍的新空闲块 。每次结合完之后,代码还要检查是否可以继续合并成更大的页面 。最佳情况是系统的空闲页面块将和允许分配的最大内存一样大 。

在图3.4中,如果释放页面框号1,它将和空闲页面框号0结合作为大小为2个页面的空闲块排入free_area的第一个元素中 。

3.5内存映射
映象执行时,可执行映象的内容将被调入进程虚拟地址空间中 。可执行映象使用的共享库同样如此 。然而可执行文件实际上并没有调入物理内存,而是仅仅连接到进程的虚拟内存 。当程序的其他部分运行时引用到这部分时才把它们从磁盘上调入内存 。将映象连接到进程虚拟地址空间的过程称为内存映射 。



图3.5 虚拟内存区域


每个进程的虚拟内存用一个mm_struct来表示 。它包含当前执行的映象(如BASH)以及指向vm_area_struct 的大量指针 。每个vm_area_struct数据结构描叙了虚拟内存的起始与结束位置,进程对此内存区域的存取权限以及一组内存操作函数 。这些函数都是Linux在操纵虚拟内存区域时必须用到的子程序 。其中一个负责处理进程试图访问不在当前物理内存中的虚拟内存(通过页面失效)的情况 。此函数叫nopage 。它用在Linux试图将可执行映象的页面调入内存时 。

可执行映象映射到进程虚拟地址时将产生一组相应的vm_area_struct数据结构 。每个vm_area_struct数据结构表示可执行映象的一部分:可执行代码、初始化数据(变量)、未初始化数据等等 。Linux支持许多标准的虚拟内存操作函数,创建vm_area_struct数据结构时有一组相应的虚拟内存操作函数与之对应 。

3.6请求换页
当可执行映象到进程虚拟地址空间的映射完成后,它就可以开始运行了 。由于只有很少部分的映象调入内存,所以很快就会发生对不在物理内存中的虚拟内存区域的访问 。当进程访问无有效页表入口的虚拟地址时,处理器将向Linux报告一个页面错误 。

页面错误带有失效发生的虚拟地址及引发失效的访存方式 。Linux必须找到表示此区域的vm_area_struct结构 。对vm_area_struct数据结构的搜寻速度决定了处理页面错误的效率,而所有vm_area_struct结构是通过一种AVL(Adelson-Velskii and Landis) 树结构连在一起的 。如果无法找到vm_area_struct与此失效虚拟地址的对应关系,则系统认为此进程访问了非法虚拟地址 。这时Linux将向进程发送SIGSEGV信号,如果进程没有此信号的处理过程则终止运行 。

如果找到此对应关系,Linux接下来检查引起该页面错误的访存类型 。如果进程以非法方式访问内存,比如对不可写区域进行写操作,系统将产生内存错误的信号 。

如果Linux认为页面出错是合法的,那么它需要对这种情况进行处理 。

首先Linux必须区分位于交换文件中的页面和那些位于磁盘上的可执行映象 。Alpha AXP的页表中有可能存在有效位没有设置但是在PFN域中有非0值的页表入口 。在这种情况下,PFN域指示的是此页面在交换文件中的位置 。如何处理交换文件中的页面将在下章讨论 。

不是所有的vm_area_struct数据结构都有一组虚拟内存操作函数,它们有的甚至没有nopage函数 。这是因为 Linux通过分配新的物理页面并为其创建有效的页表入口来修正这次访问 。如果这个内存区域存在nopage操作函数,Linux将调用它 。

一般Linux nopage函数被用来处理内存映射可执行映象,同时它使用页面cache将请求的页面调入物理内存中去 。

当请求的页面调入物理内存时,处理器页表也必须更新 。更新这些入口必须进行相关硬件操作,特别是处理器使用TLB时 。这样当页面失效被处理完毕后,进程将从发生失效虚拟内存访问的位置重新开始运行 。

推荐阅读