text: LWN：在进程级别完成内核相同页面合并（KSM）控制！
  By Jonathan CorbetApril 13, 2023DeepL assisted translationhttps://lwn.net/Articles/928510/内核相同页面合并（KSM, kernel samepage merging）功能可以在某些种类的场景中节省大量的内存，但是其存在的安全问题大大限制了它的使用。哪怕是在那些可以安全地启动 KSM 的情况下，内核所提供的控制接口也使得 KSM 不太可能被实际用起来。来自 Stefan Roesch 的一组小 patch 就希望通过改进和简化 KSM 的管理方式来改变这种情况。正如它的名字所暗示的那样，KSM 的工作原理就是寻找内容相同的内存 page，并将它们合并成同一份，由所有用户共享。正如 Avi Kivity 在 2008 年首次提出该功能时所描述的那样，早期的使用场景是 "典型的多用户 gnome 小型计算机，上面所有 150 个用户都在同时阅读 lwn.net 而不是在工作"；这种场景下会产生大量相同的 cache page，这些 page 就可以在系统范围共享而不需要重复生成了。还有其他一些使用场景，如运行相同软件的虚拟机或容器的时候，如果挑出其中最重要的工作负载，也可以得到优化。对这种重复数据进行删除可能确实会有价值。事实证明，一些工作场景下产生了大量相同的 page；合并这些 page 可以大大减少这些情况下的内存使用，允许更多的工作被塞进系统。因此，KSM 看起来很有吸引力。由于安全和专利方面的考虑，导致这一功能的合并被推迟了一段时间，但它最终在 2009 年底进入了 2.6.32 版本。然而，事实证明，这种机制本身存在的安全问题比原先想象的要多。KSM 的工作原理是扫描内存中的 page，来找出那些具有相同内容的 page。当发现这些页面时，所有的用户都将共享该页面的同一个副本，而其他重复的副本将被返回给系统的 free list。不过，必须注意一些问题： KSM 使用的是匿名页面（对应了文件内容的 page 已经通过 page cache 实现了共享），这些页面的所有者可以在任何时候改变其内容。很明显，一个进程如果修改了其中内容的话，不应该影响任何其他可能在共享该 page 的进程。为了确保这个正确行为，内核会将共享的页面标记为只读；如果一个进程对这样的 page 进行写入，那么会进行一次写时复制（COW, copy-on-write）的操作将被执行，从而给要写入的进程在此提供自己独立的副本。有了这种机制，page 就可以在互不信任的进程之间安全地共享，而这些进程完全不知道在它们背后发生了什么巧妙的处理。至少，这是它原本的意图。对一个自己独立拥有的 page 进行写入是一个非常快速的操作，而写入一个强迫 COW 操作的只读 page 则需要相当长的时间。一个恶意进程可以利用这个时间差来确定一个 page 是否被内核共享映射了；这反过来又允许攻击者确定系统中是否存在一个具有特定内容的 page。如果有足够的时间的话，这些固定时间间隔的攻击方式可以用来发现一个特定的程序是否在系统上运行，确定另一个进程的地址空间布局随机化的偏移（address-space layout randomization offsets），甚至是泄露加密密钥。与 KSM 相关的安全问题非常令人担忧，因此该功能一般不会被启用。开启它的唯一方法就是由那些希望参与共享的进程来发起 madvise() 调用（MADV_MERGEABLE），为指定的内存区域启用 KSM。很少有程序这样做，所以 KSM 没有被广泛使用。正如 Roesch 在封面邮件中指出的那样，即使是知道 KSM 并希望启用它的开发者，如果他们使用的是不提供 madvise() 的那些垃圾收集（garbage-collected）的语言，也可能无法达到这个目的。对这个问题所提出的解决方案是一个新增的 prctl()操作（PR_SET_MEMORY_MERGE），它为整个进程设置 KSM 状态。如果使用了这个操作来开启 KSM，进程中每一个可以被合并的虚拟内存区域（VMA）都将启用 KSM，在其调用后所创建的任何符合条件的 VMA 也是如此。这个设置会被任何子进程所继承，因此，比如说一个控制组或虚拟机的第一个进程启用了 KSM，所有的子进程也将启用它。该功能允许虚拟机或容器的编排系统对它所启动的工作负载都启用 KSM；KSM 的使用将不再是每个进程必须自己决定的设置了。编排系统可能对其运行的工作负载有更多的了解，从而能够确定是否可以安全地启用 KSM；而单个进程本身通常缺乏这种信息。Roesch 说，能够以这种方式启用 KSM 的系统 "展示出了 20%左右的空间增长"。换句话说，Kivity 在 15 年前描述的系统现在能够支持 180 个在摸鱼的 LWN 读者了，这对各方来说都是一个好消息。这组 patch 还做了一些其他修改，主要是为了改进 KSM 产生的效率指标，因此管理员可以确定对 page 进行扫描工作的运行时间开销比起由此节省的内存空间来说是否值得。这个 patch set 目前处于第五次修订。在以前的 review 中，大部分讨论都是关于内存节省究竟来自哪里的。在 KSM 的早期，最主要的来源是那些保持了大量 0 值填充 page 的工作负载，但现在似乎不是这样了；KSM 正在重新利用大量的非零 page。没有什么东西可以阻挡这组 patch 在不久的将来登陆 upstream。全文完LWN 文章遵循 CC BY-SA 4.0 许可协议。