简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 Kernel Shared Memory (KSM)。KSM 允许这个系统管理程序通过合并内存页面来增加并发虚拟机的数量。本文探索 KSM 背后的理念(比如存储去耦合)、KSM 的实现、以及如何管理 KSM。
软件工程可能是一个不断演变的过程:通过解决方案解决问题,解决方案产生新问题,然后再构建新的解决方案。理想的情况是,产生的新问题能够证明原始解决方案的有效性。本文讨论的技术是这种二级解决方案之一,针对由服务器虚拟化产生的问题。但是,在深入 KSM 之前,我们先快速检查一下原始解决方案,以及 KSM 如何在这里和其他地方应用。
虚拟化技术从上世纪 60 年代开始出现,经由 IBM® System/360® 大型机得以流行。50 年过后,虚拟化技术取得了跨越式发展,使得多个操作系统和应用程序共享一个服务器成为可能。这一特殊用途(称为服务器虚拟化)正在演变为数据中心,因为单个物理机能够用于托管 10 个(一般情况)或更多虚拟机(VM),如图 1 所示。这种虚拟化使基础设施更动态、更省电、(因而也)更经济。请参见 参考资料 部分了解关于服务器虚拟化及其好处的更多信息。
图 1. 通过虚拟化进行的服务器合并
如果某个物理服务器总是只使用 10-15% 的资源,那么在这个物理服务器上合并多个虚拟服务器就很有用。但是资源情况如何呢?主要的可用资源包括 CPU、内存和网络带宽。CPU 和网络通常没有得到充分利用,因此真正的问题就是可用内存。每个操作系统都有自己对可用内存资源的独特需求。由此产生的问题是:那些需求到底有多独特?
事实证明,如果您虚拟化许多相同的操作系统和应用程序组,那么许多内存页面都是相同的。假如操作系统和应用程序代码以及常量数据在 VMs 之间相同,那么这个特点就很有用。当页面惟一时,它们可以被合并,从而释放内存,供其他应用程序使用。图 2 演示了内存共享,并展示了在内容相同的 VMs 之间共享页面时更多可用闲置内存的好处。
图 2. 跨 VMs 的内存共享
您很快就会发现,尽管 Linux 中的内存共享在虚拟环境中有优势(KSM 最初设计用于基于内核的虚拟机),但它在非虚拟环境中仍然有用。事实上,KSM 甚至在嵌入式 Linux 系统中也有用处,表明了这种方法的灵活性。下面,我们将探索这种 Linux 内存共享方法,以及如何使用该方法提高服务器的内存密度,从而增加其托管其他应用程序或 VMs 的能力。
存储技术中的一个称为去耦合(de-duplication)的最新进展是 Linux 和其他系统管理程序中的内存共享的先驱。去耦合这种技术通过删除冗余数据(基于数据块,或者基于更大的数据片段,比如文件)来减少已存储的数据。公共数据片段被合并(以一种 copy-on-write [CoW] 方式),释放空间供其他用途。使用这种方法,存储成本更低,最终需要的存储器也更少。鉴于当前的数据增长速度,这个功能显得非常重要。
KSM 作为内核中的守护进程(称为 ksmd
)存在,它定期执行页面扫描,识别副本页面并合并副本,释放这些页面以供它用。KSM 执行上述操作的过程对用户透明。例如,副本页面被合并(然后被标记为只读),但是,如果这个页面的其中一个用户由于某种原因更改该页面,该用户将(以 CoW 方式)收到自己的副本。可以在内核源代码 ./mm/ksm.c 中找到 KSM 内核模块的完整实现。
KSM 依赖高级应用程序来提供指导,根据该指导确定合并的候选内存区域。尽管 KSM 可以只扫描系统中的匿名页面,但这将浪费 CPU 和内存资源(考虑到管理页面合并进程所需的空间)。因此,应用程序可以注册可能包含副本页面的虚拟区域。
KSM 应用程序编程接口(API)通过 madvise
系统调用(见清单 1)和一个新的建议参数(advice parameter)MADV_MERGEABLE
(表明已定义的区域可以合并)来实现。可以通过 MADV_UNMERGEABLE
参数(立即从一个区域取消合并任何已合并页面)从可合并状态删除一个区域。注意,通过 madvise
来删除一个页面区域可能会导致一个 EAGAIN
错误,因为该操作可能会在取消合并过程中耗尽内存,从而可能会导致更大的麻烦(内存不足情况)。
清单 1. madvise 系统调用
#include <sys/mman.h> int madvise( void *start, size_t length, int advice ); |
一旦某个区域被定义为 “可合并”,KSM 将把该区域添加到它的工作内存列表。启用 KSM 时,它将搜索相同的页面,以写保护的 CoW 方式保留一个页面,释放另一个页面以供它用。
KSM 使用的方法与内存去耦合中使用的方法不同。在传统的去耦合中,对象被散列化,然后使用散列值进行初始相似性检查。当散列值一致时,下一步是进行一个实际对象比较(本例中是一个内存比较),以便正式确定这些对象是否一致。KSM 在它的第一个实现中采用这种方法,但后来开发了一种更直观的方法来简化它。
在当前的 KSM 中,页面通过两个 “红-黑” 树管理,其中一个 “红-黑” 树是临时的。第一个树称为不稳定树,用于存储还不能理解为稳定的新页面。换句话说,作为合并候选对象的页面(在一段时间内没有变化)存储在这个不稳定树中。不稳定树中的页面不是写保护的。第二个树称为稳定树,存储那些已经发现是稳定的且通过 KSM 合并的页面。为确定一个页面是否是稳定页面,KSM 使用了一个简单的 32 位校验和(checksum)。当一个页面被扫描时,它的校验和被计算且与该页面存储在一起。在一次后续扫描中,如果新计算的校验和不等于此前计算的校验和,则该页面正在更改,因此不是一个合格的合并候选对象。
使用 KSM 进程处理一个单一的页面时,第一步是检查是否能够在稳定树中发现该页面。搜索稳定树的过程很有趣,因为每个页面都被视为一个非常大的数字(页面的内容)。一个 memcmp
(内存比较)操作将在该页面和相关节点的页面上执行。如果 memcmp
返回 0,则页面相同,发现一个匹配值。反之,如果 memcmp
返回 -1,则表示候选页面小于当前节点的页面;如果返回 1,则表示候选页面大于当前节点的页面。尽管比较 4KB 的页面似乎是相当重量级的比较,但是在多数情况下,一旦发现一个差异,memcmp
将提前结束。请参见图 3 查看这个过程的视觉呈现。
图 3. 搜索树中的页面的搜索过程
如果候选页面位于稳定树中,则该页面被合并,候选页面被释放。有关代码位于 ksm.c/stable_tree_search()
(称为 ksm.c/cmp_and_merge_page()
)中。反之,如果没有发现候选页面,则应转到不稳定树(参见 ksm.c/unstable_tree_search()
)。
在不稳定树中搜索时,第一步是重新计算页面上的校验和。如果该值与原始校验和不同,则本次扫描的后续搜索将抛弃这个页面(因为它更改了,不值得跟踪)。如果校验和没有更改,则会搜索不稳定树以寻找候选页面。不稳定树的处理与稳定树的处理有一些不同。第一,如果搜索代码没有在不稳定树中发现页面,则在不稳定树中为该页面添加一个新节点。但是如果在不稳定树中发现了页面,则合并该页面,然后将该节点迁移到稳定树中。
当扫描完成(通过 ksm.c/ksm_do_scan()
执行)时,稳定树被保存下来,但不稳定树则被删除并在下一次扫描时重新构建。这个过程大大简化了工作,因为不稳定树的组织方式可以根据页面的变化而变化(还记得不稳定树中的页面不是写保护的吗?)。由于稳定树中的所有页面都是写保护的,因此当一个页面试图被写入时将生成一个页面故障,从而允许 CoW 进程为写入程序取消页面合并(请参见 ksm.c/break_cow()
)。稳定树中的孤立页面将在稍后被删除(除非该页面的两个或更多用户存在,表明该页面还在被共享)。
如前所述,KSM 使用 “红-黑” 树来管理页面,以支持快速查询。实际上,Linux 包含了一些 “红-黑” 树作为一个可重用的数据结构,可以广泛使用它们。“红-黑” 树还可以被 Completely Fair Scheduler (CFS) 使用,以便按时间顺序存储任务。您可以在 ./lib/rbtree.c 中找到 “红-黑” 树的这个实现。
KSM 的管理和监控通过 sysfs(位于根 /sys/kernel/mm/ksm)执行。在这个 sysfs 子目录中,您将发现一些文件,有些用于控制,其他的用于监控。
第一个文件 run 用于启用和禁用 KSM 的页面合并。默认情况下,KSM 被禁用(0
),但可以通过将一个 1
写入这个文件来启用 KSM 守护进程(例如,echo 1 > sys/kernel/mm/ksm/run
)。通过写入一个 0
,可以从运行状态禁用这个守护进程(但是保留合并页面的当前集合)。另外,通过写入一个 2
,可以从运行状态(1
)停止 KSM 并请求取消合并所有合并页面。
KSM 运行时,可以通过 3 个参数(sysfs 中的文件)来控制它。sleep_millisecs 文件定义执行另一次页面扫描前 ksmd
休眠的毫秒数。max_kernel_pages 文件定义 ksmd
可以使用的最大页面数(默认值是可用内存的 25%,但可以写入一个 0
来指定为无限)。最后,pages_to_scan 文件定义一次给定扫描中可以扫描的页面数。任何用户都可以查看这些文件,但是用户必须拥有根权限才能修改它们。
还有 5 个通过 sysfs 导出的可监控文件(均为只读),它们表明 ksmd
的运行情况和效果。full_scans 文件表明已经执行的全区域扫描的次数。剩下的 4 个文件表明 KSM 的页面级统计数据:
- pages_shared:KSM 正在使用的不可交换的内核页面的数量。
- pages_sharing:一个内存存储指示。
- pages_unshared:为合并而重复检查的惟一页面的数量。
- pages_volatile:频繁改变的页面的数量。
KSM 作者定义:较高的 pages_sharing/pages_shared 比率表明高效的页面共享(反之则表明资源浪费)。
Linux 并不是使用页面共享来改进内存效率的惟一系统管理程序,但是它的独特之处在于将其实现为一个操作系统特性。VMware 的 ESX 服务器系统管理程序将这个特性命名为 Transparent Page Sharing (TPS),而 XEN 将其称为 Memory CoW。不管采用哪种名称和实现,这个特性都提供了更好的内存利用率,从而允许操作系统(KVM 的系统管理程序)过量使用内存,支持更多的应用程序或 VM。 您可以在最新的 2.6.32 Linux 内核中发现 KSM — 以及其他很多有趣的特性。
学习
- Red Hat 的 KSM 作者撰写了一篇很好的关于 KSM 的论文,标题为 “Increasing memory density by using KSM”(PDF),这篇论文介绍了 KSM,并深入研究了它的实现的一些细节。
- 作为一种节约能源并降低成本的方法,虚拟化正在迅猛发展,虚拟化还是云计算架构的一个关键元素。参阅 “通过虚拟化促进绿色计算:虚拟化是绿色 IT 的基础”(developerWorks,2009 年 8 月),了解虚拟化及其在绿色 IT 中的角色。
- 本文展示了将 Linux 作为一个系统管理程序使用的优势。参阅 “剖析 Linux hypervisor”(developerWorks,2009 年 5 月)、“探索 Linux 内核虚拟机”(developerWorks,2007 年 4 月)和 “虚拟 Linux”(developerWorks,2006 年 12 月),了解这个主题和其他主题。最后,参阅 “Linux 2.6 Completely Fair Scheduler 内幕”(developerWorks,2009 年 12 月),了解使用 “红-黑” 树的其他内核子系统。
- 您可以从 2.6.32 内核发布说明 了解关于内存去耦合的更多信息。这些说明提供了关于 KSM 的有用材料(包括一个到内核文档的链接)以及 2.6.32 版的各种其他变化。在 KSM 部分,您将发现一个有趣的参考,介绍如何在一个 RAM 为 16 GB 的服务器上运行 52 个 Windows® XP VMs(其中每个 VM 被分配 1GB RAM 并与 KSM 共享)。
- 参阅 IT 分析 文章 “De-dupefor big storage savings?”,了解关于内存系统的去耦合及其方法的更多信息。
- Wikipedia 提供了一个不错的 去耦合简介,包括各种实现策略(比如源和目标)。
- 尽管 KSM 是在虚拟服务器中减小内存足迹的自动机制,但是应用程序开发人员有时也拥有类似的手动操作能力。Linux 动态(或共享)库允许二进制文件使用公共库对象,而不是由应用程序静态编译的库对象。参阅 “Linux 动态库剖析”(developerWorks,2008 年 8 月),了解关于共享库的更多信息。
- “红-黑” 树(或对称二进制 B 树)是 Rudolf Bayer 发明的一个自平衡二进制树。它是一个有用的树表示,对于插入、搜索和删除等操作,它拥有很好的 “最坏情况时间(worst-case time)” 。“红-黑” 树广泛应用于各种应用程序,其中包括关联数组的构造。
- M. Tim Jones 是一位多产作者,更多内容请参考 M. Tim Jones 专栏。
- 在 developerWorks Linux 专区 寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料,查阅我们 最受欢迎的文章和教程。
- 在 developerWorks 上查阅所有 Linux 技巧 和 Linux 教程。
- 随时关注 developerWorks 技术活动和网络广播。
- 观看 developerWorks 演示中心,包括面向初学者的产品安装和设置演示,以及为经验丰富的开发人员提供的高级功能。
获得产品和技术
- 以最适合您的方式 IBM 产品评估试用版软件:下载产品试用版,在线试用产品,在云环境下试用产品,或者在 IBM SOA Sandbox for People 中花费几个小时来学习如何高效实现 Service Oriented Architecture。
讨论
- 加入 My developerWorks 社区。查看开发人员参与的博客、论坛、组和 wikis,并与其他 developerWorks 用户交流。