2015年12月14日星期一

Things about KSM

       虚拟机对于宿主机而言是个黑盒,所以即使明知虚拟机之间存在很多冗余数据,宿主并不清楚内容相同的页在内存中的位置。这被称为semantic gap。跨越这个鸿沟的一个解决方案是使用内存扫描器找到共享机会。内存扫描器周期性的扫描虚拟机内存以寻找所有内容相同的页以便于共享它们。
      
传统页共享
       在最先进的基于UNIX的操作中传统上使用两种方式节省空间:第一个是复制进程的地址空间在所有clones之间是cow共享的;第二是共享库通过mmap系统调用从磁盘映射,在所有的进程之间是只读共享的,于是每个库在系统内存中只存在一份。
mmap存在一个重要的限制:只有在页源自同一个文件(有相同的inode)时才能实现内存共享。只要文件被拷贝inode改变,那些文件的mmap会在OS的page cache中创建复制页。

虚拟化环境中的页共享
不幸的是,这两种方法都不能直接适用于虚拟化环境。在虚拟化环境中宿主并不了解客户机的内部状态。半虚拟化环境中客体机被修改以配合宿主协同工作,修改过的客体机会提供给宿主其内部状态相关的信息,以便于宿主可采取适当的措施实现不同虚拟机间以及虚拟机与宿主间的共享内存页。
KSM是通用的内存扫描器和相同内存合并器,它工作在匿名页上。KSM要扫描的内存首先要通过madvise系统调用被建议至KSM。建议过的内存页会被一页一页的线性的周期性的扫描。所有建议页的一个完整的扫描被定义为一个扫描回合scan round。对于每个要扫描的页会计算该页的校验和checksum。如果校验和从上次扫描至此时保持不变,那么这个页会被标记为潜在的共享候选页sharing candidate。这些备选页会被扫描以查看它们的内容是否与stable tree中的另一个页匹配,在稳定数中保存着所有当前已共享的页。假如在稳定树中没有这样的页,那么潜在的共享页会继续与不稳定树总的结点比较,在不稳定树中包含的是所有潜在sharing candidates。假如还是没有发现相同页,一个新的页就会被插入unstable tree里。假如发现匹配页,共享伙伴会从非稳定树中被移除并且两个页会被共享然后插入到稳定树中。
KSM有一个不变的扫描开销,一定数量的CPU总是被KSM内核进程使用。KSM周期性的唤醒,扫描固定数目的页然后进程再次挂起直至下次唤醒。KSM扫描线程是单发射的。内存扫描器总是要在节省内存与CPU使用量之间做折中。

KSM进程分成4个阶段:scan, compare, merge和unmerge。 KSM并不是为虚拟机专门设计的,实际上只要应用的内存是可合并的都能使用KSM。KSM的实现上只能合并匿名页而不适用命名页named pages。命名页有一个关联的backing file,例如通过mmap映射的文件。由于虚拟机的内存对于宿主而言是个黑盒,虚拟机的所有内存页对于宿主都是匿名页。因此,这些页可被KSM合并。

Scan阶段
每隔sleep_millisecs KSM就会扫描pages_to_scan个页。所有建议内存区域的完整扫描称为一个scan round。对于每个要扫描的虚拟页,反向映射项会被创建。使用反向映射项rmap_item保存该虚拟页的状态信息——页内容校验和,页是否已共享。每个虚拟页在系统中都有且只有一个rmap_item表示。KSM的扫描进程需要重用rmap_item当要重新扫描虚拟页时。每个被扫描的虚拟页都会通过KSM的比较程序与现存的共享候选页相比较以发现有相同内容的页。

Compare阶段
比较程序检查在系统中是否已存在与页相同内容的页。KSM使用两个红黑树结构作为查找结构:stable tree包含所有已合并页的指针,而unstable tree包含所有潜在的sharing candidate。虚拟页不像对象有representation,使用相应的rmap_item被用来向树中插入。在红黑树中使用页内容排序。在比较程序的开始,rmap_item可能处于三种状态中的一个:已共享,不稳定和易变。假如反向映射项是当前已共享的,不需要任何进一步的工作过程结束。假如反向映射项是不稳定,表明它已被标记为潜在的共享候选并且已经被插入了unstable tree。如果是易变volatile,它可能在将来称为共享候选页。
在比较过程的开始,将rmap_item从unstable tree中移除,假如反向映射项本不在非稳定树中,那么不做任何事。然后开始把它与稳定树进行比较,假如发现匹配,该页会被合并进稳定树并结束比较程序。不能在稳定树中找到匹配页,就转向非稳定树继续查找。假如存在匹配项,那么两页会被合并进稳定树并且相应项从非稳定树中移除。假如没找到匹配项,该共享备选页会被插入到非稳定树中。在完成整个扫描回合后,非稳定树会被flushed,同时清理了所有的潜在共享备选项。

Merge阶段
合并例程将若干相同内容的虚拟页映射至一个物理页 并且将所映射的物理页标记为cow。此过程将rmap_item插入稳定树。未映射的重复物理页被释放。由于稳定树中的结点依靠页内容索引,几个rmap_items会连接到同一个树结点上。为了解决这个问题,rmap_item会为每个物理页的反向映射项组织成单向链表。

Unmerge阶段
假如应用需要写一个当前已共享的页,操作系统会从MMU得到一个异常,异常处理程序会打破页共享使得进程得到一个专属的私有页的可读可写拷贝。另外,新得到的相应页会被标记为非共享的。它会在下次search/insert操作时从稳定树中被移除,同时设置其对应的rmap_item为易变的。假如它是最后一个共享伙伴,那么稳点树中的结点也要从树中移除。

KSM中的数据结构

KSM使用全局的扫描游标ksm_scan,在KSM进程休眠时存储扫描过程中状态信息。为了能够线性的扫描每一个虚拟内存区域,KSM使用rmap_item链表代表每个虚拟地址空间中的虚拟页状态。一个rmap_item包含了虚拟地址,校验和以及当前每个页的各自状态(volatile, unstable, stable)。rmap_item链表不是在内存建议至KSM时创建的,而是伴随着扫描过程实时构建起来的。最后被扫描的rmap_item的位置会在扫描游标结构体中被存储下来。从这个位置开始扫描链表寻找与记录的地址匹配的rmap_item。假如找不到匹配项,则会在链表尾端添加一个这个地址的新rmap_item项。每个虚拟地址空间的rmap_items组织成一个链表。KSM可以动态的增长advised内存空间。扫描游标结构体包含一个指向当前要扫描地址空间(mm_slot)的指针,虚拟地址,以及当前要扫描的rmap_item。每个地址空间都创建一个rmap_item链表然后存储到mm_slot里。ksm_scan数据结构也包含了扫描回合数信息,这个序列码可被用于判断一个rmap_item何时进行的最后一次比较。
KSM会在进程退出时清除属于该进程的虚拟内存区域VMA。 这个过程与线性扫描是并发进行的,KSM使用扫描游标避免竞争:属于已退出进程地址空间的rmap_item只有当它们不属于当前要扫描地址空间一部分的情况下才能被移除。








没有评论:

发表评论