页交换分析

一、什么是页交换

        在现代操作系统中,内存资源是有限的,而运行的程序和数据往往需要占用大量内存空间。为了能让更多的程序可以并发运行,并且让系统高效利用内存,操作系统采用了虚拟内存的概念。虚拟内存将磁盘空间的一部分作为内存的扩展,使得程序可以拥有比实际物理内存更大的 “虚拟” 存储空间。

        当内存不足的时候,把最近很少访问的没有存储设备支持的物理页(内核页面或者一些无法交换的页面,如内存映射的I/O区域、锁定的内存等)的数据暂时保留到交换区中,释放内存空间,当交换区中的存储页被访问的时候,再把数据从交换区读入到内存中。其中交换区可以是一个磁盘分区,也可以是存储设备(机械硬盘、固态硬盘以及NAND闪存)上的一个文件。

        其中固态硬盘使用NAND内存作为存储介质,NAND闪存的特点:在写入数据之前,需要把块中的内容擦除,然而每个块的擦除次数优先,大概是10的5次方到10的6次方次,因此频繁地写数据会缩短闪存的寿命。

        因此使用固态硬盘或者NAND闪存并不适合启用交换区;如果使用机械硬盘存储数据,可以启用交换区。

二、内核中的数据结构

1.交换区头部结构体swap_header

        swap_header 是用于交换区(swap space)头部的结构体,描述的是 整个交换空间 的元数据,即所有交换区域(比如多个交换分区或交换文件)。主要包含有关交换区格式和元数据的魔术数字、版本信息等。它用于交换区的头部,定义了交换区格式、版本、UUID 等信息。用于标识和验证交换区的正确性。

举个例子:

假设你的系统配置了两个交换分区 /dev/sda1/dev/sdb1,那么:

  1. swap_header 描述了整个交换空间的元数据,可能包括了这两个交换分区的基本信息,如:

    • 交换空间的总容量
    • 交换设备的数量(在这个例子中是 2)
    • 交换块的大小

       每个字段的含义如上图。

2.交换区信息swap_info_struct

        swap_info_struct 是 Linux 内核中用于描述交换空间(swap)的信息的结构体。它存储了交换区的状态、大小、设备信息等。它用于管理和表示交换设备的相关信息,内核通过它来控制和访问交换空间。

        内核定义了交换区信息数组swap_info,每个数组项存储一个交换区的信息。数组项的数量是在编译时有红MAX_SWAPFILES指定的,通常是32,说明最多启用32个交换区。在操作系统的内存管理中,交换区会被进一步划分成多个连续的单元,这里将其称为 “槽(SLOT)”。

        每个槽的长度被设定为等于页(Page)的长度。页是内存管理中的基本单位,无论是物理内存还是虚拟内存,都会按照固定大小划分成一个个页面,比如常见的页面大小可能是 4KB、8KB 等。交换区划分出的这些槽,和内存中的页大小一致,这样做的好处在于方便进行数据的交换和管理操作。

        例如,当内存中某个页面的数据需要被换出到交换区时,就可以直接对应放置到交换区的一个槽中;反之,当要从交换区把数据换回到内存时,也能准确地将一个槽中的数据加载到对应的内存页面上,使得虚拟内存系统中内存与交换区之间的数据交互能够高效、有序地进行。

        交换区按照优先级从高到低排序,首先从优先级高的交换区分配槽位。对于优先级相同的交换区,轮流从每个交换区分配槽位,每次从交换区分配槽位之后,把交换区移到优先级相同的交换区的最后面的位置。

       

重要字段:

    unsigned long  flags;      //标志位

    signed short    prio;       //优先级

    struct plist_node list;     //用于加入有效交换区链表,按照优先级操作

    signed char type;           //交换区的索引

    unsigned int    max;        //交换区的最大页数

    unsigned int pages;            //交换区正在使用的页的数量

    unsigned int lowest_bit;   //数组中第一空闲数组项的索引.

    unsigned int highest_bit;   //数组中最后一个空闲项的索引.

    unsigned char *swap_map;

        这个结构体格外重要,它指向的是一个数组,这个数组中的每一个字节都和交换区中的每一个槽位(SLOT)一一对应。也就是说,假设有 N 个槽位的交换区,那么对应的 swap_map 所指向的数组就会有 N 个字节。

低六位用于存储使用计数器:

        每个字节的低六位(也就是二进制表示下的最低六位)专门用于存储每个槽位的使用计数器,这个计数器代表的是共享换出页的进程数量。

        例如,当一个内存页面被换出到交换区的某个槽位时,可能有多个进程之前都使用了这个内存页面, 那么这些进程在逻辑上都 “共享” 了这个换出到交换区的页面,此时这个计数器就会相应地增加,记录共享该页面的进程个数。

        当有进程不再需要访问这个换出的页面,或者某个进程结束了,对应的共享计数就会减少。通过这种方式,内核可以清楚地知道每个槽位对应的换出页面被多少个进程所依赖,这对于内存管理中的页面回收等操作有着重要意义。比如,当决定要回收某个槽位对应的页面时,如果这个计数器值为 0,意味着没有进程再需要这个页面了,就可以安全地将其从交换区清除或者重新分配给其他要换出的页面使用;而如果计数器值不为 0,则需要谨慎处理,可能要等待所有共享该页面的进程都不再使用它之后才能进行回收操作。

高两位用作标志位实现页的交换缓存:

        每个字节的高两位(二进制表示下的最高两位)被定义为标志位,用于辅助实现页的交换缓存机制。例如:

        脏页标志(Dirty Page Flag):用于指示对应槽位中的页面数据自上次从内存换出后是否被修改过。

        锁定标志(Lock Flag):可以用来标记对应槽位中的页面是否处于被锁定状态.。

        缓存有效性标志(Cache Validity Flag):用于表明该槽位对应的页面在交换缓存中的有效性情况。

unsigned int cluster_next; //当前聚集中下一次分配的槽位

关于 “聚集(cluster)” 概念的解释

        在交换区的管理中,“聚集” 是一种组织和分配槽位的策略。通常情况下,为了减少磁盘 I/O 操作的碎片化以及提高数据读写的性能, 不会孤立地去分配单个槽位给换出的页面,而是将多个连续的槽位组合在一起形成一个 “聚集”。当内存中有页面需要换出到交换区时, 优先在这些连续的槽位聚集中进行分配,这样做的好处是,在后续进行页面换入操作或者对这些换出页面相关的数据进行读写时, 由于它们在磁盘上(也就是交换区中)的位置相对集中、连续,磁盘磁头寻道等操作的开销会相对较小,能更高效地完成数据的传输,进而提升整个虚拟内存系统的运行效率。

unsigned int cluster_nr;   //当前聚集中可用的槽位数量

struct rb_root swap_extent_root;//这个结构体与下面的swap_extent相关联

3.交换区间swap_extent

        交换区间swap_extent用来把交换区的连续槽位映射到连续的磁盘块。如果交换区是磁盘分区,因为磁盘分区的块是连续的,所以只需要一个交换区间。如果交换区是文件,因为文件对应的磁盘块不一定是连续的,所以对于每个连续的磁盘块范围,需要使用一个交换区间来存储交换区的连续槽位和磁盘块范围的映射关系。

        struct swap_extent 结构体在操作系统的交换区管理中起着关键作用,主要用于建立交换文件(这里的交换文件既可以是块设备,也可以是常规的普通文件,只要满足可作为交换区使用的条件,即 IS_REG 文件)中页面范围与磁盘块范围之间的映射关系。通过一系列这样的结构体组成的链表(或者说通过红黑树等数据结构组织起来的集合,借助 rb_node 可实现类似链表有序组织的功能),就能完整地描述整个交换文件对应的页面与磁盘块的映射情况,从而为交换区中数据的读写、页面的换入换出等操作提供准确的物理位置信息依据。

        其中rb_node就作为一个节点,将整个swap_extent结构体加入到对应的swap_info_struct中。

4.交换槽位缓存swap_slots_cache

        为了加快为换出页分配交换槽的速度,每个处理器有一个交换槽位缓存swap_slots_cache数据结构。

        slots指向交换槽位的数组,nr是空闲槽位的数量,cur是当前已经分配的槽位数量,也是下一次分配的数组索引,alloc_lock用来保护slots,nr,cur三个成员。

        为了换出页分配交换槽位的时候,首先从当前处理器的交换槽位缓存分配,如果缓存没有空位,那么从交换区分配槽位以重新填充交换位缓存。交换槽位缓存并不是位于处理器内部的物理部件中,而是在操作系统的内存管理体系中,利用内存资源开辟出的一块缓存区域,用于更高效地管理交换区中的槽位分配等操作。它本质上是在内存里的数据结构及相关缓存信息,目的是减少频繁直接访问磁盘交换区带来的性能开销。

        在为换出页分配交换槽位的过程中,会优先从当前处理器对应的交换槽位缓存中进行分配,这是因为缓存中已经记录了一些之前管理交换区槽位时的相关状态信息(比如哪些槽位是空闲的等情况),直接从缓存里分配会比每次都去磁盘的交换区查找空闲槽位速度更快。但如果缓存中没有空闲的空位了(也就是 nr 为 0,没有可分配的空闲槽位了),此时就需要从磁盘上的交换区去分配槽位,然后把这些新分配到的槽位相关信息更新到交换槽位缓存中,重新填充交换位缓存,以便后续继续高效地从缓存里进行槽位分配操作。

        以下是对于当内存中的页面需要被交换出去时,这个结构体字段的访问流程:

  1. 锁定机制

    • 在操作 swap_slots_cache 之前,首先会进行锁定,以确保对缓存的并发访问安全。
      • alloc_lock:这个互斥锁保护了 slotsnrcur 等字段,防止多个线程同时修改缓存状态。
      • free_lock:用于保护 slots_retn_ret 等字段,防止并发操作时造成数据一致性问题。
  2. 检查是否有可用槽位

    • 内核首先需要检查 swap_slots_cache 中是否有可用的槽位来存储页面。这可以通过查看 nr 字段来完成。
      • nr:表示当前缓存中可用的槽位数量。如果 nr 大于 0,则说明有可用槽位。
  3. 分配槽位

    • 如果有可用的槽位,内核将通过 slots 数组来访问可用的槽位。cur 字段会指示当前正在使用的槽位的索引位置。
      • cur:表示当前操作的槽位索引。内核将使用该索引来确定槽位的位置。
    • 使用 cur 来获取一个空闲槽位(即 slots[cur]),并进行相应的页面交换操作。比如,将内存页面的数据写入该槽位。
  4. 更新槽位状态

    • 当内存页面被交换到磁盘时,内核会更新 swap_slots_cache 中的状态:
      • slots[cur]:表示分配给该页面的槽位,内核会在这里记录这个页面的交换位置。
      • nr:可用槽位的数量减少,nr 递减。
      • cur:当前槽位的索引位置也可能增加,指向下一个槽位。

5.交换项swp_entry_t

        内核定义数据类型swp_entry_t以存储换在交换区中的位置,我们称为交换项高7位存储交换区的索引,其他位存储交换区中的偏移(单位为页)。

6.交换缓存

        每一个交换区有若干个交换缓存,2的14次方页对应一个交换缓存,交换缓存的数量是是交换区总页数的2的14次方分之一。例如,如果交换区总共包含的页数是 2 的 18 次方页,那么通过计算(2 的 18 次方除以 2 的 14 次方)可以得出交换缓存的数量就是 16 个。

        之所以需要交换缓存,是因为换出页可能由多个进程共享,进程的页表项存储在交换区中的位置。当某个进程访问页的数据时,把页从交换区换入内存中,把页表项指向内存页。具体原因如下:

  1. 多进程共享换出页的情况
    在多进程运行的操作系统环境下,常常会出现多个进程共享某些页面数据的情况。当内存紧张时,这些被多个进程共享的页面可能会被换出到交换区中暂存。而每个进程都有自己的页表,页表项用于记录页面在内存中的位置等信息,当页面被换出到交换区后,页表项就需要存储该页面在交换区中的相应位置信息,以便后续能准确地找回这些页面。

  2. 交换缓存的作用体现
    交换缓存的存在就是为了更好地处理这种多进程共享页面以及页面换入换出的情况。当某个进程需要访问之前被换出到交换区的页面数据时,系统首先会在交换缓存中查找该页面是否已经被缓存起来了(因为有可能之前别的共享该页面的进程已经触发过将其从交换区换入内存的操作,并且相关信息被暂存在交换缓存中了)。如果在交换缓存中找到了,就可以直接依据缓存中的信息快速地将该页面从交换区换入内存中,同时更新该进程的页表项,使其指向内存中的这个页面,这样就避免了每次都要从整个交换区去查找页面所在位置然后再换入内存的繁琐过程,极大地提高了页面换入的速度,进而提升整个系统在处理多进程共享页面数据时的性能和效率。

三、总结

        总的来说,整个设备中有多个交换区,swap_header就描述了整个设备中交换空间的配置,而对于每一个交换区有一个特定的swap_info_struct与之对应,而每一个交换分区里面有多个连续的空间就是swap_extent。

        紧接着内核里还有交换槽位缓存swap_slots_cache数据结构。这里面描述的是交换槽位,这个槽位里面存的是交换空间中的一个物理位置,该位置可以用于存储被交换出去的内存页面。而具体位置存储在里面的swp_entry_t字段中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值