📄 00000004.htm
字号:
map_nr <BR> 记录本mem_map_t描叙的物理页面框号。 <BR>页面分配代码使用free_area数组来寻找和释放页面,此机制负责整个缓冲管理。另外此 <BR> <BR>代码与处理器使用的页面大小和物理分页机制无关。 <BR>free_area中的每个元素都包含页面块的信息。数组中第一个元素描叙1个页面,第二个 <BR> <BR>表示2个页面大小的块而接下来表示4个页面大小的块,总之都是2的次幂倍大小。list域 <BR> <BR>表示一个队列头,它包含指向mem_map数组中page数据结构的指针。所有的空闲页面都在 <BR> <BR>此队列中。map域是指向某个特定页面尺寸的页面组分配情况位图的指针。当页面的第N <BR>块 <BR>空闲时,位图的第N位被置位。 <BR>free_area结构。第一个元素有个自由页面(页面框号0),第二个元素有4个页面大小的 <BR> <BR>2个自由块,前一个从页面框号4开始而后一个从页面框号56开始。 <BR>3.4.1 页面分配 <BR>Linux使用Buddy算法来有效的分配与回收页面块。页面分配代码每次分配包含一个或 <BR>者多个物理页面的内存块。页面以2的次幂的内存块来分配。这意味着它可以分配1个、 <BR> <BR>2个和4个页面的块。只要系统中有足够的空闲页面来满足这个要求(nr_free_pages> <BR>min_free_page),内存分配代码将在free_area中寻找一个与请求大小相同的空闲块。 <BR>free_area中的每个元素保存着一个反映这样大小的已分配与空闲页面的位图。例如, <BR>free_area数组中第二个元素指向一个反映大小为四个页面的内存块分配情况的内存映 <BR>象。 <BR>分配算法首先搜寻满足请求大小的页面。它从free_area数据结构的list域着手沿链来 <BR>搜索空闲页面。如果没有这样请求大小的空闲页面,则它搜索两倍于请求大小的内存块 <BR>。 <BR>这个过程一直将持续到free_area被搜索完或找到满足要求的内存块为止。如果找到的 <BR>页面块大于请求的块则对其进行分割以使其大小与请求块匹配。由于块大小都是2的次 <BR>幂所以分割过程十分简单。空闲块被连进相应的队列而这个页面块被分配给调用者。 <BR>当系统中有大小为两个页面块的请求发出时,第一个4页面大小的内存块(从页面框号 <BR>4开始)将分成两个2页面大小的块。前一个,从页面框号4开始的,将分配出去返回给 <BR>请求者,而后一个,从页面框号6开始,将被添加到free_area数组中表示两个页面大小 <BR> <BR>的空闲块的元素1中。 <BR>3.4.2 页面回收 <BR>将大的页面块打碎进行分配将增加系统中零碎空闲页面块的数目。页面回收代码在适当 <BR> <BR>时机下要将这些页面结合起来形成单一大页面块。事实上页面块大小决定了页面重新组 <BR> <BR>合的难易程度。 <BR>当页面块被释放时,代码将检查是否有相同大小的相邻或者buddy内存块存在。如果有, <BR> <BR>则将它们结合起来形成一个大小为原来两倍的新空闲块。每次结合完之后,代码还要检 <BR> <BR>查是否可以继续合并成更大的页面。最佳情况是系统的空闲页面块将和允许分配的最大 <BR> <BR>内存一样大。 <BR>如果释放页面框号1,它将和空闲页面框号0结合作为大小为2个页面的空闲块排入 <BR>free_area的第一个元素中。 <BR>3.5 内存映射 <BR>映象执行时,可执行映象的内容将被调入进程虚拟地址空间中。可执行映象使用的共享 <BR> <BR>库同样如此。然而可执行文件实际上并没有调入物理内存,而是仅仅连接到进程的虚拟 <BR> <BR>内存。当程序的其他部分运行时引用到这部分时才把它们从磁盘上调入内存。将映象连 <BR> <BR>接到进程虚拟地址空间的过程称为内存映射。 <BR>每个进程的虚拟内存用一个mm_struct来表示。它包含当前执行的映象(如BASH)以及指 <BR> <BR>向vm_area_struct的大量指针。每个vm_area_struct数据结构描叙了虚拟内存的起始与 <BR> <BR>结束位置,进程对此内存区域的存取权限以及一组内存操作函数。这些函数都是Linux在 <BR> <BR>操纵虚拟内存区域时必须用到的子程序。其中一个负责处理进程试图访问不在当前物理 <BR> <BR>内存中的虚拟内存(通过页面失效)的情况。此函数叫nopage。它用在Linux试图将可执行 <BR> <BR>映象的页面调入内存时。 <BR>可执行映象映射到进程虚拟地址时将产生一组相应的vm_area_struct数据结构。每个 <BR>vm_area_struct数据结构表示可执行映象的一部分:可执行代码、初始化数据(变量)、 <BR> <BR>未初始化数据等等。Linux支持许多标准的虚拟内存操作函数,创建vm_area_struct数据 <BR> <BR>结构时有一组相应的虚拟内存操作函数与之对应。 <BR>3.6 请求换页 <BR>当可执行映象到进程虚拟地址空间的映射完成后,它就可以开始运行了。由于只 <BR>有很少部分的映象调入内存,所以很快就会发生对不在物理内存中的虚拟内存区 <BR>域的访问。当进程访问无有效页表入口的虚拟地址时,处理器将向Linux报告一 <BR>个页面错误。 <BR>页面错误带有失效发生的虚拟地址及引发失效的访存方式。Linux必须找到表示此 <BR>区域的vm_area_struct结构。对vm_area_struct数据结构的搜寻速度决定了处理 <BR>页面错误的效率,而所有vm_area_struct结构是通过一种AVL(Adelson-Velskii <BR>and Landis)树结构连在一起的。如果无法找到vm_area_struct与此失效虚拟地址 <BR>的对应关系,则系统认为此进程访问了非法虚拟地址。这时Linux将向进程发送 <BR>SIGSEGV信号,如果进程没有此信号的处理过程则终止运行。 <BR>如果找到此对应关系,Linux接下来检查引起该页面错误的访存类型。如果进程 <BR>以非法方式访问内存,比如对不可写区域进行写操作,系统将产生内存错误的 <BR>信号。 <BR>如果Linux认为页面出错是合法的,那么它需要对这种情况进行处理。 <BR>首先Linux必须区分位于交换文件中的页面和那些位于磁盘上的可执行映象。 <BR>Alpha AXP的页表中有可能存在有效位没有设置但是在PFN域中有非0值的页表入 <BR>口。在这种情况下,PFN域指示的是此页面在交换文件中的位置。如何处理交换 <BR>文件中的页面将在下章讨论。 <BR>不是所有的vm_area_struct数据结构都有一组虚拟内存操作函数,它们有的甚 <BR>至没有nopage函数。这是因为Linux通过分配新的物理页面并为其创建有效的页 <BR>表入口来修正这次访问。如果这个内存区域存在nopage操作函数,Linux将调用 <BR>它。 <BR>一般Linuxnopage函数被用来处理内存映射可执行映象,同时它使用页面cache <BR>请求的页面调入物理内存中去。 <BR>当请求的页面调入物理内存时,处理器页表也必须更新。更新这些入口必须进 <BR>行相关硬件操作,特别是处理器使用TLB时。这样当页面失效被处理完毕后,进 <BR>程将从发生失效虚拟内存访问的位置重新开始运行。 <BR>3.7 Linux页面cache <BR>Linux使用页面cache的目的是加快对磁盘上文件的访问。内存映射文件以每次 <BR>一页的方式读出并将这些页面存储在页面cache中。图3.6表明页面cache由 <BR>page_hash_table,指向mem_map_t数据结构的指针数组组成。 <BR>Linux中的每个文件通过一个VFS inode(在文件系统一章中讲叙)数据结构 <BR>来标识并且每个VFSinode都是唯一的,它可以并仅可以描叙一个文件。页表的 <BR>索引从文件的VFSinode和文件的偏移中派生出来。 <BR>从一个内存映射文件中读出页面,例如产生换页请求时要将页面读回内存中, <BR>系统尝试从页面cache来读出。如果页面在cache中,则返回页面失效处理过程 <BR>一个指向mem_map_t数据结构;否则此页面将从包含映象的文件系统中读入内存 <BR>并为之分配物理页面。 <BR>在映象的读入与执行过程中,页面cache不断增长。当不再需要某个页面时, <BR>即不再被任何进程使用时,它将被从页面cache中删除。 <BR>3.8 换出与丢弃页面 <BR>当系统中物理内存减少时,Linux内存管理子系统必须释放物理页面。这个任务 <BR>由核心交换后台进程(kswapd)来完成。 <BR>核心交换后台进程是一种特殊的核心线程。它是没有虚拟内存的进程,在物理 <BR>地址空间上以核心态运行。核心交换后台进程的名字容易使人误解,其实它完 <BR>成的工作比仅仅将页面交换到系统的交换文件中要多得多。其目标是保证系统 <BR>中有足够的空闲页面来维持内存管理系统运行效率。 <BR>此进程由核心的init进程在系统启动时运行,被核心交换定时器周期性的调用。 <BR>当定时器到时后,交换后台进程将检查系统中的空闲页面数是否太少。它使用 <BR>两个变量:free_pages_high和free_page_low来判断是否该释放一些页面。只 <BR>要系统中的空闲页面数大于free_pages_high,核心交换后台进程不做任何工 <BR>作;它将睡眠到下一次定时器到时。在检查中,核心交换后台进程将当前被写 <BR>到交换文件中的页面数也计算在内,它使用nr_async_pages来记录这个数值; <BR>当有页面被排入准备写到交换文件队列中时,它将递增一次,同时当写入操作 <BR>完成后递减一次。如果系统中的空闲页面数在free_pages_high甚至free_pages_low <BR>以下时,核心交换后台进程将通过三个途径来减少系统中使用的物理页面的个数: <BR> 减少缓冲与页面cache的大小, <BR> 将系统V类型的内存页面交换出去, <BR> 换出或者丢弃页面。 <BR>如果系统中空闲页面数低于free_pages_low,核心交换后台进程将在下次运行之 <BR>前释放6个页面。否则它只释放3个。以上三种方法将依次使用直到系统释放出足 <BR>够的空闲页面。当核心交换后台进程试图释放物理页面时它将记录使用的最后一 <BR>种方法。下一次它会首先运行上次最后成功的算法。 <BR>当释放出足够页面后,核心交换后台进程将再次睡眠到下次定时器到时。如果导 <BR>致核心交换后台进程释放页面的原因是系统中的空闲页面数小于free_pages_low, <BR>则它只睡眠平时的一半时间。一旦空闲页面数大于free_pages_low则核心交换进 <BR>程的睡眠时间又会延长。 <BR>3.8.1 减少Page Cache和Buffer Cache的大小 <BR>Page Cache和Buffer cache中的页面将被优先考虑释放到free_area数组中。Page <BR>Cache中包含的是内存映射文件的页面,其中有些可能是不必要的,它们浪费了系 <BR>统的内存。而Buffer,Cache中包含的是从物理设备中读写的缓冲数据,有些可能 <BR>也是不必要的。当系统中物理页面开始耗尽时,从这些cache中丢弃页面比较简单 <BR>(它不需要象从内存中交换一样,无须对物理设备进行写操作)。除了会使对物理 <BR>设备及内存映射文件的访问速度降低外,页面丢弃策略没有太多的副作用。如果策 <BR>略得当,则所有进程的损失相同。 <BR>每次核心交换后台进程都会尝试去压缩这些cache。 <BR>它首先检查mem_map页面数组中的页面块看是否有可以从物理内存中丢弃出去的。当 <BR>系统中的空闲页面数降低到一个危险水平时,核心后台交换进程频繁进行交换
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -