你可以把一台物理服务器想象成一间合租大屋:几十个租客(虚拟机)各自有房间,房间里的桌椅书架都是内存页。管理员发现,每个房间都放着一本一模一样的《新人手册》,与其买几十本占地方,不如只留一本放在公共书架上,谁要看就去翻——这就是内存去重。另一个场景是,有的房间空荡荡,浪费了不少空间;管理员拿出一个大号气球塞进房间,把没人用的空位占住,再把省下的空间借给隔壁房间;等屋主需要时,放掉气球把空间还回去——这就是内存气球。两种机制一静一动、一省一挪,让有限的物理内存在大量虚拟机之间高效流转。
对 AI 从业者而言,理解 KSM 与 Ballooning,意味着你能看懂云平台“内存超售”背后的底层逻辑,也是排查虚拟机性能抖动、设计高密度资源池的基本功。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
KSM去重原理
假设你住在一间合租大屋里,每位室友床头都放着一模一样的《新人手册》。管理员巡视一圈后发现,与其买十本占地方,不如只留一本放到公共书架上,大家要看随时来翻。在 Linux 内核里,KSM(Kernel Samepage Merging)做的正是这件事:它把一个进程或虚拟机使用的匿名内存页当作“书页”,周期性扫描,凡是内容完全相同的页,就合并成同一个物理页,然后在多个虚拟地址映射到这份唯一副本。被合并的页是只读的,后续的写操作会走写时复制。
从成本角度看,合并省下的内存等于重复页数乘以单页大小。比如重复了 8 页,每页 4KB,就省下 32KB。现实中虚拟机的内核代码段、共享库、空白页到处都是“同款书”,KSM 能在宿主机上省出 GB 级内存。这也是云平台敢做超售的底气之一。
回到合租屋的比喻:管理员把十本《新人手册》收掉九本,省下的书架空间,正好就是“重复书本数 × 每本书厚度”。KSM 的收益底层逻辑也是如此直白。
- 重复页数 N_duplicateKSM 扫描到的、与其他页内容完全相同的匿名页数量。
- 单页大小 S_page每个内存页固定的容量,x86_64 上通常为 4KB。
- 节省内存 N_saved相同页合并后物理内存只剩一份,省下的字节数等于重复页数乘页大小。
stable树机制
公共书架上的书越来越多,管理员不能每收一本都跟所有书逐字对比,否则会累死。于是,他为每一本已经稳定的书编了一份目录,按内容的特征快速定位。KSM 把合并后的稳定共享页挂在一棵红黑树(stable 树)上,先用内容哈希值作为“目录编号”找到候选节点,再做完整字节比较确认,避免哈希碰撞导致误合并。
红黑树是平衡二叉搜索树,每次查找最多花费 O(log n) 步,这里的 n 指 stable 树中共享页节点的数量。页表改指向后,这些节点会长期存在,成为后续扫描的参照物。换句话说,stable 树既是“目录”,也是“已合并书页的集合”。
对照生活经验:图书馆的目录卡片不是按书名逐页翻出来的,而是按字母排序后折半查找。stable 树就是这本“会自动平衡的目录”,让管理员在成千上万本书中也能一眼定位。
- 查找时间 T_search一次 KSM 扫描中,从 stable 树定位匹配页所需的比较次数。
- 节点数量 nstable 树中已合并的不同内容共享页数量。
- 对数复杂度 O(log n)红黑树保持平衡,搜索路径长度与树高同级,即节点数量的对数。
写时复制回退
公共书架上唯一一本《新人手册》不能让大家随便写,只能看。如果某位室友想在书上画重点,管理员会先复印一本给他,再让他在复印件上写;其他人仍看原版。KSM 的合并页也是这样:物理页只读,页表项去掉写权限。
当某个进程真的写共享页时,CPU 触发写保护异常,内核分配一个新物理页,把原共享页内容复制过去,并让发起写入的进程页表指向新页,随后重新以可写方式执行;其他进程仍指向原共享页。这个机制叫写时复制(Copy-on-Write,COW)。发生写回退的页越多,说明这个共享页的“稳定性”越差,KSM 的收益也就越打折扣。
回到合租屋:如果大家只是翻书,一本公共书足够;如果每个人都抢着在书上写心得,管理员就得不停复印,最终反而浪费纸。COW 的价值,就是让“看的人多”和“写的人少”这两个前提成立。
- 共享页数 N_shared当前被多个进程映射的 KSM 合并页数量。
- 写回退比例 r_write在一段观察期内,共享页被写入并触发 COW 的概率或比例。
- 私有页数 N_private因写操作从共享页拆出来的新物理页数量。
气球驱动原理
管理员还有一个大号气球。他会把气球塞进某个房间,在房间里充气,占掉屋主暂时用不到的空地;这些被占的地方并不是偷来的,而是屋主暂时不需要的闲置区域。等屋主需要时,管理员再放气,把空间完整还回去。Ballooning 的原理与之几乎一样。
Guest 虚拟机内部有一个气球驱动模块,通过虚拟设备与宿主机通信。当 Host 内存紧张时,Host 请求充气:Guest 中的气球驱动申请并锁定一批内存页,使 Guest 可用内存减少,同时这些页被释放给 Host 复用。当 Guest 内存不足时,Host 请求放气:气球驱动释放锁定的页,归还给 Guest。气球当前占用的净内存,就是累计充气量减去累计放气量。
这和合租屋完全一致:管理员不会搬走你正在用的床和桌子,只会把角落里常年不用的空箱子收走,记一笔“暂借账”。等你说“我要用箱子了”,他立刻放气归还。
- 累计充气量 M_inflate气球驱动从 Guest 回收并锁定的内存总量。
- 累计放气量 M_deflate气球驱动释放并归还给 Guest 的内存总量。
- 气球净占用 M_balloon当前 Guest 暂时让出给 Host 的内存大小。
宿主调度协作
房东管家会盯着整栋楼的水压表:如果某几间房同时水龙头大开,压力就不够用了。他会到闲置较多的房间充气球,把空间“借调”给紧缺房间;等紧缺缓解或对方要用水,再放气还回去。宿主机上的内存调度也是如此。
宿主机监控全局内存水位,当可用内存低于阈值,便向 Guest 发送充气请求,引导 Guest 归还空闲页;多个 Guest 之间可以错峰借还。Ballooning 最大的价值是让动态内存分配不必走“关机、拔内存条、重启”的老路,也不需要热插拔虚拟内存条。配合超售策略,宿主机可以在不重启虚拟机的情况下,把一台 Guest 的空闲内存调度给另一台更需要的 Guest。
这个机制帮宿主“削峰填谷”:有的 VM 内存闲着,有的 VM 内存吃紧,气球就像管道一样把空闲内存导过去。当然,管道不能超过物理总量,否则水压问题依然会爆发为 OOM。
- 基础空闲内存 M_host_base未借助气球时,宿主机自身空闲的物理内存。
- 气球净借入 M_balloon所有 Guest 当前让给 Host 使用的内存总和。
- 宿主可用内存 M_host_free经过调度后,宿主机实际可用于新负载的内存总量。
开销与权衡
管理员来回翻书、搬书、复印都要花时间;如果大家整天在公共书架上涂改,管理员反而更累。KSM 并不是免费的:扫描内存页、计算哈希、更新 stable 树、修改页表都会消耗 CPU,页表竞争锁也可能拖慢内核路径。
更关键的是延迟敏感负载。数据库等应用的高频写入会让 KSM 合并的页反复触发 COW,产生缺页异常和内存复制,造成延迟抖动。因此,KSM 是否开启要看收益模型:重复内容多、写入少,收益远大于开销;反之,管理员就该“罢工”,通过 sysfs 将 /sys/kernel/mm/ksm/run 置为 0,关闭 KSM。
回到合租屋:如果公共书架上全是大家每天都要涂改的笔记,管理员不停复印反而把纸都浪费了。与其强行共享,不如让每个人各买各的书。延迟敏感负载正是“天天涂改的室友”,KSM 在他们身上常常得不偿失。
- 总开销 C_overheadKSM 运行消耗的 CPU 时间与其他资源代价。
- 扫描页数 N_scanKSM 周期性扫描的内存页数量。
- 合并事件数 N_merge包括成功合并和写回退在内的页操作次数。
- 系数 α、β两种操作各自的单位成本,表示扫描一次和合并一次需要付出多少 CPU 代价。
应用场景优化
不是所有宿舍都适合放共享书架:有些房间全是白墙,有些堆满私人物品,管理员需要分类施策。KSM 适合大量相同镜像/操作系统的场景,例如桌面虚拟化、云主机池和容器集群,因为 Guest 的内核、共享库、空白页高度重复,去重收益非常大。
Ballooning 则更适合动态负载明显的场景,比如多租户超售环境。实际优化时要注意:透明大页(THP)与大页的引入会改变页粒度,KSM 通常只处理 4KB 匿名页;NUMA 拓扑下,跨节点访问共享页会引入远端内存延迟,需要结合 CPU 亲和性做取舍。最终是否开启去重,可以用“净节省”来评估。
运维同学可以定期读取 KSM 的统计节点,观察 pages_shared 和 full_scans,如果发现合并页少而 CPU 占用高,就应该果断关闭或缩小扫描范围。这就像管理员定期盘点:共享书架的借阅量太低,就该撤掉,把地方腾出来放别的东西。
- 毛收益 N_duplicate × S_page理论上合并重复页后省出的内存空间。
- 运行开销 C_overheadKSM 扫描、树操作、页表更新等付出的 CPU 成本。
- 净节省 S_net把成本扣除后,系统实际获得的内存收益。
KSM与气球协同
管理员的最优操作是先整理书架,再吹气球:先把重复的书合并成一本,空出书架;再把房间里真正没人用的空箱子收进气球,借给隔壁。KSM 和 Ballooning 协同,正是“一减一挪”。KSM 先合并内容相同的页,使每个 Guest 实际占用的物理内存下降;Ballooning 再把 Guest 的空闲页回收给 Host,放进统一的内存池。
两者结合时要注意顺序:最好先让 KSM 稳定合并重复页,再让 Ballooning 回收那些没有被映射的空闲页。如果先放气/回收了共享页映射,可能触发 COW,导致重复页重新出现,反而增大内存压力。实践中,KSM 减少了 Guest 的工作集,Ballooning 的充气压力也会变小;Ballooning 释放出的内存,又能支撑更多新 Guest。两种机制互相成就。
合租大屋的两位管理员终于配合默契:一位负责把重复的书变成公共书,一位负责把闲置的空地变成可调度的气球。物理内存还是那么多,但每一块都被安排得明明白白。
- KSM 节省 M_ksm通过合并相同内容页所释放的物理内存。
- 气球节省 M_balloon气球从各 Guest 手中借出、供 Host 统一调度的内存。
- 总收益 M_total去重与借还叠加后,宿主机额外可用的内存空间。
AI 应用场景
虚拟化平台内存超售
阶段:资源池构建与调度
解决的问题:传统方式下,虚拟机数量受物理内存限制,内存利用率低。KSM 将同镜像虚拟机的重复页合并,Ballooning 再回收各 Guest 的空闲页,让超售比例安全提升。
具体案例:某云平台部署 100 台相同 CentOS 虚拟机,开启 KSM 后,共享库和内核页大量合并,宿主空闲内存增加约 30%;再配合 Ballooning,将低负载 VM 的内存临时调度给高峰业务,虚拟机密度提升近一倍。
延迟敏感数据库保护
阶段:性能保障与调优
解决的问题:数据库内存页频繁写入,KSM 的写时复制会造成抖动;传统做法只能关闭 KSM,但浪费了重复页收益。通过内存 cgroup 隔离、NUMA 亲和性设置以及限制 Ballooning 收页速率,可以兼顾内存利用率与尾延迟。
具体案例:金融云数据库实例通过将关键 VM 标记为“不可合并”,保留 KSM 用于其他普通 VM;同时设置气球充气上限,避免突发回收影响 SQL 查询延迟。
Serverless与容器运行时
阶段:运行时隔离与弹性伸缩
解决的问题:Serverless 平台中大量函数实例运行在轻量虚拟机里,重复运行时库很多。KSM 可以跨实例合并相同代码页,Ballooning 则负责在函数实例缩容后把内存还给宿主,降低冷启动带来的内存峰值。
具体案例:Kata Containers 安全容器场景下,相同基础镜像启动的 Pod 共享内核页,配合 Ballooning 在 Pod 销毁后快速回收内存,使单机 Pod 密度提升 40% 以上。
Python 演示
import hashlibfrom collections import defaultdict PAGE_SIZE = 4096 class MemoryPage: def __init__(self, data): self.data = data self.digest = hashlib.sha256(data).hexdigest() def ksm_merge(pages): groups = defaultdict(list) for idx, page in enumerate(pages): groups[page.digest].append(idx) owner_map = {} merged = 0 for digest, indices in groups.items(): owner = indices[0] for idx in indices[1:]: owner_map[idx] = owner merged += 1 freed = merged * PAGE_SIZE return freed, merged, owner_map def balloon_reclaim(guest_free_mb, need_mb): inflate = min(guest_free_mb, need_mb) deflate = 0 return inflate, deflate def main(): pages = [] for i in range(100): if i % 4 == 0: data = b"same" * 1024 else: data = bytes([i]) * 4096 pages.append(MemoryPage(data)) freed, merged, owner_map = ksm_merge(pages) print("merged pages:", merged) print("owner map sample:", dict(list(owner_map.items())[:3])) inflate, deflate = balloon_reclaim(guest_free_mb=512, need_mb=300) net_balloon = inflate - deflate print("balloon net MB:", net_balloon) total_mb = freed / (1024 * 1024) + net_balloon print("total saved MB:", total_mb) if __name__ == "__main__": main()
groups[page.digest].append(idx)按页内容哈希分组,就像把内容相同的“书页”归到同一个书架。owner = indices[0]每个分组的第一页作为保留下的共享页,其余页都指向它。owner_map[idx] = owner记录重复页真正所有者的映射,模拟 KSM stable 树中的页表改指向。freed = merged * PAGE_SIZE重复页数乘页大小,就是去重省出的内存字节数。inflate = min(guest_free_mb, need_mb)气球最多只能拿走 Guest 的空闲量,不能强抢正在使用的页。total_mb = freed / (1024 * 1024) + net_balloon把 KSM 去重收益与气球借还收益合在一起,看到整体节省。
常见误区
误区KSM 能把所有相同内容的内存都合并,所以应该无条件开启,参数设得越激进越好,反正内存越多越划算。
正解KSM 只能处理可写的匿名页,且要经过扫描、stable 树匹配和页表更新;内核页、页缓存等不在合并范围内。更关键的是,合并后任何写入都要触发写时复制,频繁写入会让 CPU 开销暴涨。是否开启应看重复页收益与扫描成本之间的净差额。
为何常见:因为“相同页=省内存”太直观,初学者容易忽略扫描与 COW 带来的性能税。
误区气球充气时,宿主机就像强盗一样把 Guest 正在运行的内存页抢走,虚拟机随时会卡死或崩溃。
正解Ballooning 遵循协商机制:Guest 只把宣告为空闲的页交给气球驱动,正在被进程使用的页不会被强行夺走。如果 Guest 内存紧张,Host 会放气归还,甚至 Guest 可以拒绝继续充气。它更像管理员搬走空箱子,而不是抢你手里的手机。
为何常见:把“向 Guest 要内存”理解成“强行剥夺”,忽略了气球驱动是一个双向协商的虚拟设备。
误区只要同时开启去重和气球,宿主机内存就永远够用,超售多少都行,不会再出现内存耗尽。
正解KSM 和 Ballooning 只是提高内存利用率,并不会凭空增加物理内存总量。如果超售比例超过真实物理容量,又赶上所有虚拟机同时进入高峰期,宿主机依然会触发 swap 甚至 OOM Killer。两者是“省着花”和“拆东墙补西墙”,不是印钞机。
为何常见:把内存优化当成内存扩容,混淆了“利用率”和“容量”两个概念。
误区既然大家共享同一个物理页,那么某个进程写入后,其他共享进程也能直接看到新数据,性能和共享内存一样。
正解KSM 的共享页是只读的,任何写入都会先触发 COW:内核复制一个私有页,再让写者映射到新页面并写入。其他进程仍然指向原共享页,看到的还是旧内容。多进程之间不会互相污染,否则数据安全就完全失控了。
为何常见:把 KSM 的“只读共享”和传统 IPC 的“可写共享内存”混为一谈,忽略了页表权限这一层保护。
面试题
Q1. 下列哪类内存页最容易被KSM合并?
- A.完全相同的匿名页← 正确答案
- B.同一进程的栈页
- C.页缓存中的文件页
- D.互不相同的纹理页
答案:A。KSM 扫描的是匿名内存页,且要求两个页内容逐字节完全相同。就像管理员只把同款书合并,不同内容的书不能硬塞到公共书架。栈页通常频繁变化,文件页归页缓存管理,纹理页内容各异,所以 A 最合适。
Q2. KSM把相同页合并后,物理页的权限被标记为什么?
- A.标记为只读页← 正确答案
- B.标记为读写页
- C.标记为只执行页
- D.标记为全局页
答案:A。合并后的物理页被多个进程共享,必须只读,否则一个进程写入会污染其他进程。一旦有人要写,就触发写时复制,拆出一份私有页再写。
Q3. stable树在KSM中的主要作用是?
- A.快速查找可合并的稳定页← 正确答案
- B.管理虚拟CPU调度队列
- C.跟踪磁盘块缓存的信息
- D.维护进程地址空间链表
答案:A。stable 树保存已经合并的共享页,KSM 扫描候选页时通过哈希和树搜索快速判断能否合并。它不管理 CPU 调度、磁盘缓存或进程链表,所以选 A。
Q4. 下列哪些情况会增加KSM的CPU开销?
- A.页内容频繁变化引起写回退← 正确答案
- B.多个进程共享后反复触发COW← 正确答案
- C.可合并页数量大导致树操作多← 正确答案
- D.物理内存长期处于空闲状态
答案:ABC。A 和 B 都是写时复制回退,分配新页、复制内容、改页表都要 CPU;C 中大量可合并页让 stable 树查找和插入变频繁。D 空闲状态下 KSM 也要扫描,但不会大量合并和写回退,整体开销相对不显著,所以选 ABC。
Q5. 某共享页首次被进程写入时,写时复制回退的第一步是什么?
- A.分配新页并复制原内容← 正确答案
- B.将原物理页内容清空
- C.立即撤销所有页的映射
- D.向用户进程发送信号
答案:A。写共享只读页会触发缺页异常,内核先分配一个私有物理页,把原共享页内容复制过去,再更新页表并重试写指令。清空原页或撤销所有映射会让其他进程失去公共书,信号机制也不是 COW 的步骤。
Q6. Ballooning充气时,Guest会把哪些内存页交给Host?
- A.自己标记为空闲的页← 正确答案
- B.内核刚启动时的页
- C.正在DMA传输中的页
- D.所有只读属性的页
答案:A。气球驱动遵循协商流程:Guest 认为暂时不用的空闲页才会被锁定并移交给 Host。正在 DMA 或内核使用的页不会交出,只读属性也不代表空闲。
Q7. Host与Guest中的balloon驱动之间通常通过什么通信?
- A.虚拟PCI设备通道← 正确答案
- B.网络套接字消息
- C.共享内存信标区
- D.硬件中断线触发
答案:A。Balloon driver 在 Guest 内部以虚拟 PCI 设备的形式存在,Host 通过该设备发送充气/放气请求。网络套接字、共享内存和普通硬件中断都不是标准的 balloon 通信机制。
Q8. 以下关于KSM与Ballooning协同的说法,正确的是?
- A.KSM去重可减少Guest实际页占用← 正确答案
- B.Ballooning可回收空闲页给Host← 正确答案
- C.写回退会使回收页彻底失效
- D.二者都增加物理内存总量
答案:AB。KSM 把相同页合并成一份,Guest 实际占用下降;Ballooning 再回收空闲页,两者是“去重+借还”的配合。写回退只是把某个共享页拆成私有页,不会让整个回收机制失效;它们都不能增加物理内存总量。
Q9. 下列哪种场景更适合关闭KSM?
- A.高频写入的延迟敏感数据库← 正确答案
- B.大量相同虚拟机镜像的场景
- C.只读共享库为主的容器
- D.CPU核数较少的宿主主机
答案:A。数据库页频繁写入,KSM 合并后写回退频繁,省下的内存抵不上抖动代价。B 和 C 都是重复内容多、写少,适合开启 KSM;D 不直接决定是否关闭。
Q10. 当Host内存严重不足且Ballooning无法从Guest回收足够页时,可能发生什么?
- A.Guest可能触发回收与swap← 正确答案
- B.Balloon强制锁定正在使用页
- C.Host最终会调用OOM Killer← 正确答案
- D.内存总线频率自动提升
答案:AC。Guest 收到充气请求后如果空闲页不够,会先做内部内存回收和 swap,这是“交不出书就先整理房间”。Balloon 不会强制抢正在使用的页;Host 若依然缺页,最终会触发 OOM Killer 终止进程。D 与内存总线无关。