Linux 原来是这么管理内存的( 四 )


交换区缓存仅仅已修改(脏页)被保存在交换文件中
只要这些页面在写入交换文件后没有修改,则下次交换该页面时,无需将其写入交换文件,因为该页面已在交换文件中 。可以直接丢弃 。在大量交换的系统中,这节省了许多不必要的和昂贵的磁盘操作 。
硬件缓存处理器中通常使用一种硬件缓存 。页表条目的缓存 。在这种情况下,处理器并不总是直接读取页表,而是根据需要缓存页的翻译 。这些是转换后备缓冲区 也被称为 TLB,包含来自系统中一个或多个进程的页表项的缓存副本 。
引用虚拟地址后,处理器将尝试查找匹配的 TLB 条目 。如果找到,则可以将虚拟地址直接转换为物理地址,并对数据执行正确的操作 。如果处理器找不到匹配的 TLB 条目,它通过向操作系统发信号通知已发生 TLB 丢失获得操作系统的支持和帮助 。系统特定的机制用于将该异常传递给可以修复问题的操作系统代码 。操作系统为地址映射生成一个新的 TLB 条目 。清除异常后,处理器将再次尝试转换虚拟地址 。这次能够执行成功 。
使用缓存也存在缺点,为了节省精力,Linux 必须使用更多的时间和空间来维护这些缓存,并且如果缓存损坏,系统将会崩溃 。
Linux 页表Linux 假定页表分为三个级别 。访问的每个页表都包含下一级页表

Linux 原来是这么管理内存的

文章插图
 
图中的 PDG 表示全局页表,当创建一个新的进程时,都要为新进程创建一个新的页面目录,即 PGD 。
要将虚拟地址转换为物理地址,处理器必须获取每个级别字段的内容,将其转换为包含页表的物理页的偏移量,并读取下一级页表的页框号 。这样重复三次,直到找到包含虚拟地址的物理页面的页框号为止 。
Linux 运行的每个平台都必须提供翻译宏,这些宏允许内核遍历特定进程的页表 。这样,内核无需知道页表条目的格式或它们的排列方式 。
页分配和取消分配对系统中物理页面有很多需求 。例如,当图像加载到内存中时,操作系统需要分配页面 。
系统中所有物理页面均由 mem_map 数据结构描述,这个数据结构是 mem_map_t 的列表 。它包括一些重要的属性
  • count :这是页面的用户数计数,当页面在多个进程之间共享时,计数大于 1
  • age:这是描述页面的年龄,用于确定页面是否适合丢弃或交换
  • map_nr :这是此mem_map_t描述的物理页框号 。
页面分配代码使用 free_area向量查找和释放页面,free_area 的每个元素都包含有关页面块的信息 。
页面分配Linux 的页面分配使用一种著名的伙伴算法来进行页面的分配和取消分配 。页面以 2 的幂为单位进行块分配 。这就意味着它可以分配 1页、2 页、4页等等,只要系统中有足够可用的页面来满足需求就可以 。判断的标准是nr_free_pages> min_free_pages,如果满足,就会在 free_area 中搜索所需大小的页面块完成分配 。free_area 的每个元素都有该大小的块的已分配页面和空闲页面块的映射 。
分配算法会搜索请求大小的页面块 。如果没有任何请求大小的页面块可用的话,会搜寻一个是请求大小二倍的页面块,然后重复,直到一直搜寻完 free_area 找到一个页面块为止 。如果找到的页面块要比请求的页面块大,就会对找到的页面块进行细分,直到找到合适的大小块为止 。
因为每个块都是 2 的次幂,所以拆分过程很容易,因为你只需将块分成两半即可 。空闲块在适当的队列中排队,分配的页面块返回给调用者 。
Linux 原来是这么管理内存的

文章插图
 
如果请求一个 2 个页的块,则 4 页的第一个块(从第 4 页的框架开始)将被分成两个 2 页的块 。第一个页面(从第 4 页的帧开始)将作为分配的页面返回给调用方,第二个块(从第 6 页的页面开始)将作为 2 页的空闲块排队到 free_area 数组的元素 1 上 。
页面取消分配上面的这种内存方式最造成一种后果,那就是内存的碎片化,会将较大的空闲页面分成较小的页面 。页面解除分配代码会尽可能将页面重新组合成为更大的空闲块 。每释放一个页面,都会检查相同大小的相邻的块,以查看是否空闲 。如果是,则将其与新释放的页面块组合以形成下一个页面大小块的新的自由页面块 。每次将两个页面块重新组合为更大的空闲页面块时,页面释放代码就会尝试将该页面块重新组合为更大的空闲页面 。通过这种方式,可用页面的块将尽可能多地使用内存 。


推荐阅读