页缓存
页缓存用于加快对磁盘上图像和数据的访问
它用于一次一页地缓存文件中的内容,并且可以通过文件和文件中的偏移量进行访问。当页面从磁盘读入内存时,它们被缓存在页面缓存中。
交换区缓存
仅仅已修改(脏页)被保存在交换文件中
只要这些页面在写入交换文件后没有修改,则下次交换该页面时,无需将其写入交换文件,因为该页面已在交换文件中。可以直接丢弃。在大量交换的系统中,这节省了许多不必要的和昂贵的磁盘操作。
硬件缓存
处理器中通常使用一种硬件缓存。页表条目的缓存。在这种情况下,处理器并不总是直接读取页表,而是根据需要缓存页的翻译。这些是转换后备缓冲区
也被称为 TLB
,包含来自系统中一个或多个进程的页表项的缓存副本。
引用虚拟地址后,处理器将尝试查找匹配的 TLB 条目。如果找到,则可以将虚拟地址直接转换为物理地址,并对数据执行正确的操作。如果处理器找不到匹配的 TLB 条目, 它通过向操作系统发信号通知已发生 TLB 丢失获得操作系统的支持和帮助。系统特定的机制用于将该异常传递给可以修复问题的操作系统代码。操作系统为地址映射生成一个新的 TLB 条目。清除异常后,处理器将再次尝试转换虚拟地址。这次能够执行成功。
使用缓存也存在缺点,为了节省精力,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 的次幂,所以拆分过程很容易,因为你只需将块分成两半即可。空闲块在适当的队列中排队,分配的页面块返回给调用者。
如果请求一个 2 个页的块,则 4 页的第一个块(从第 4 页的框架开始)将被分成两个 2 页的块。第一个页面(从第 4 页的帧开始)将作为分配的页面返回给调用方,第二个块(从第 6 页的页面开始)将作为 2 页的空闲块排队到 free_area 数组的元素 1 上。
页面取消分配
上面的这种内存方式最造成一种后果,那就是内存的碎片化,会将较大的空闲页面分成较小的页面。页面解除分配代码会尽可能将页面重新组合成为更大的空闲块。每释放一个页面,都会检查相同大小的相邻的块,以查看是否空闲。如果是,则将其与新释放的页面块组合以形成下一个页面大小块的新的自由页面块。每次将两个页面块重新组合为更大的空闲页面块时,页面释放代码就会尝试将该页面块重新组合为更大的空闲页面。通过这种方式,可用页面的块将尽可能多地使用内存。
例如上图,如果要释放第 1 页的页面,则将其与已经空闲的第 0 页页面框架组合在一起,并作为大小为 2页的空闲块排队到 free_area 的元素 1 中
内存映射
内核有两种类型的内存映射:共享型(shared)
和私有型(private)
。私有型是当进程为了只读文件,而不写文件时使用,这时,私有映射更加高效。但是,任何对私有映射页的写操作都会导致内核停止映射该文件中的页。所以,写操作既不会改变磁盘上的文件,对访问该文件的其它进程也是不可见的。
按需分页
一旦可执行映像被内存映射到虚拟内存后,它就可以被执行了。因为只将映像的开头部分物理的拉入到内存中,因此它将很快访问物理内存尚未存在的虚拟内存区域。当进程访问没有有效页表的虚拟地址时,操作系统会报告这项错误。
页面错误描述页面出错的虚拟地址和引起的内存访问(RAM)类型。
Linux 必须找到代表发生页面错误的内存区域的 vm_area_struct 结构。由于搜索 vm_area_struct 数据结构对于有效处理页面错误至关重要,因此它们以 AVL(Adelson-Velskii和Landis)
树结构链接在一起。如果引起故障的虚拟地址没有 vm_area_struct
结构,则此进程已经访问了非法地址,Linux 会向进程发出 SIGSEGV
信号,如果进程没有用于该信号的处理程序,那么进程将会终止。
然后,Linux 会针对此虚拟内存区域所允许的访问类型,检查发生的页面错误类型。如果该进程以非法方式访问内存,例如写入仅允许读的区域,则还会发出内存访问错误信号。
现在,Linux 已确定页面错误是合法的,因此必须对其进行处理。