使能KSM

KSM只会处理通过madvise系统调用显式指定的用户进程地址空间,因此用户程序想使用这个功能就必须在分配地址空间时显式地调用madvise(addr,length,MADV_MERGEA BLE)。如果用户想在KSM中取消某一个用户进程地址空间的合并功能,也需要显式地调用madvise(addr,length,MADV_UNMERGEABLE)。 下面是测试KSM的test.c程序的代码片段,使用mmap():来创建一个文件的私有映射,并且调用memset()写入这些私有映射的内容缓存页面中。

《测试KSM的test.c程序的代码片段》
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
char *buf;
char filename[64]="";
struct stat stat;
int size =100*4096;
int fd =0; strcpy(filename, argv[1]); fd = open(filename,O_RDWR | O_CREAT,0664); fstat(fd, &stat); buf = mmap (NULL,stat.st_size,PROT_WRITE,MAP_PRIVATE,fd,0); memset(buf,0x55,stat.st_size); madvise(buf,stat.st_size, MADV_MERGEABLE); while (1)
sleep(1);
}

编译上述test.c程序。

gcc test.c -o test

使用dd命令创建一个ksm.dat文件,即创建100MB大小的文件。

echo 1 >/sys/kernel/mm/ksm/run

运行test.c程序。

#./test ksm.dat

过一段时间之后,查看系统有多少页面合并了。

root@benshushu#cat /sys/kernel/mm/ksm/pages_sharing
25500
root@benshushu#cat /sys/kernel/mm/ksm/pages_shared
100
root@benshushu:/home#cat /sys/kernel/mm/ksm/pages_unshared
0

可以看到pages_shared为100说明系统有100个共享的页面。若有100个页面的内同,它们可以合并成一个页面,这时pages_shared为1。 pages_sharing 为25500说明有25500个页面合并了。 100MB的内存可存放25600个页面。因此,我们可以看到,KSM把这25600个页面分别合并成1共享的页面,每一个共享页面里共享了其他的255个页面,为什么会这样?我们稍后详细解析。 pages_unshared表示当前未合并页面的数量。

rootebenshushut cat /sys/kernel/mn/ksm/stable_node_chains
1
rootebenshushut cat /sys/kernel/mm/ksm/stable_node_dups
100

stable_node_chains表示包含了链式的稳定节点的个数,当前系统中为1,说明只有一个链式的稳定节点,但是这个稳定的节点里包含了链表。 stable_node_dups表示稳定的节点所在的链表包含的元素总数。 KSM的sysfs节点在/sys/kernel/mm/ksm/目录下,其主要节点的描述如下所示。

  • run:可以设置为0~2。若设置0,暂停ksmd内核线程;若设置1,启动ksmd内核线程;若设置2,取消所有已经合并好的页面
  • full_scans: 完整扫描和合并区域的次数
  • pages_volatile: 表示还没扫描和合并的页面数量。若由于页面内容更改过快导致两次计算的校验值不相等,那么这些页面是无法添加到红黑树里的
  • sleep_millisecs: ksmd内核线程扫描一次的时间间隔
  • pages_to_scan: 单次扫描页面的数量
  • pages_shared: 合并后的页面数。如果100个页面的内容相同,那么可以把它们合并成一个页面,这时pages_shared的值为1
  • pages_sharing: 共享的页面数。如果两个页面的内容相同,它们可以合并成一个页面,那么有一个页面要作为稳定的节点,这时pages_shared的值为1,pages_sharing也为1。第3个页面也合并进来后,pages_sharing的值为2,表示两个页面共享同一个稳定的节点
  • pages_unshared: 当前未合并页面数量
  • max_page_sharing: 这是在Linux4.3内核中新增的参数,表示一个稳定的节点最多可以合并的页面数量。这个值默认是256
  • stable_node_chains: 链表类型的稳定节点的个数。每个链式的稳定节点代表页面内容相同的KM页面。这个链式的稳定节点可以包含多个dup成员,每个dup成员最多包含256个共享的页面
  • stable_node_dups: 链表中dup成员的个数。这些dup成员会连接到链式的稳定节点的hlist链表中

KSM在初始化时会创建一个名为ksmd的内核线程。

<mm/ksm.c>
static int __init ksm_init(void)
{
ksm_thread = kthread_run(ksm_scan_thread, NULL, "ksmd");
}
subsys_initcall(ksm_init);

在tes.c程序中创建私有映射(MAP_PRIVATE)之后,显式地调用madvise系统调用把用户进程地址空间添加到 Linux内核的KSM系统中。

<madvise()->ksm_madvise()-> ksm_enter()>

int __ksm_enter(struct mm_struct *mm)
{
mm_slot = alloc_mm_slot();
insert_to_mm_slots_hash(mm, mm_slot);
list_add_tail(&mm slot->mm_list, &ksm_scan.mm_slot->mm list);
set_bit(MME_VM_MERGEABLE, &mm->flags);
}

ksm_enter()函数会把当前的 mm_struct数据结构添加到 mm_slots_hash哈希表中。另外把 mm_slot添加到 ksm_scan.mm_slot->mm_list 链表中。最后,设置mm->flags中的 MMF_VM_MERGEABLE标志位,表示这个进程已经被添加到KSM系统中.

<ksm内核线程>

static int ksm_scan_thread(void *nothing)
{
while (!kthread_should_stop())
if (ksmd_should_run())
ksm_do_scan(ksm_thread_pages_to_scan)
if (ksmd_should_run()) {
sleep_ms =READ_ONCE(ksm_thread_sleep_millisecs);
wait_event_interruptible_timeout(ksm_iter_wait,
sleep_ms != READ_ONCE(ksm_thread_sleep_millisecs),
secs_to_jiffies(sleep_ms));
}
return 0;
}

ksm_scan_thread()是ksmd内核线程的主干,它运行 ksm_do_scan()函数,扫描和合并100个页面,见 ksm_thread_pages_to_scan参数,然后等待20ms,见 ksm_thread_sleep_millisecs参数,这两个参数可以在/sys/kernel/mm/ksm目录下设置和修改。

<ksmd内核线程>

static void ksm_do_scan(unsignd int scan_npages)
{
while(scan_npages-- && likely(!freezing(current))) {
cond_resched();
rmap_item = scan_get_next_rmap_item(&page);
if (!rmap_item)
return;
cmp_and_merge_page(page, rmap_item);
put_page(page)
}
}

ksm_do_scan()函数在while循环中尝试合并scan_npages个页面, scan_get_next_rmap_item()获取一个合适的匿名页面。 cmp_and_merge_page()函数会让页面在KSM中稳定和不稳定的两棵红黑树中查找是否有可以合并的对象,并且尝试合并他们。

KSM基本实现

为了让读者先有一个初步的认识,本节先介绍Lnux4.13内核之前的KSM实现,后文会介绍Linux5.0内核中的实现。

KSM机制下采用两棵红黑树来管理扫描的页面和己经合并的页面。第一棵红黑树称为不稳定红黑树,里面存放了还没有合并的页面;第二棵红黑树称为稳定红黑树,已经合并的页面会生成一个节点,这个节点为稳定节点。如两个页面的内容是一样的,KSM扫描并发现了它们,因此这两个页面就可以合并成一个页面。对于这个合并后的页面,会设置只读属性,其中一个页面会作为稳定的节点挂载到稳定的红黑树中之后,另外一个页面就会被释放了。但是这两个页面的 rmap_item数据结构会被添如到稳定节点中的 hist 链表中,如下图所示。

我们假设有3个VMA(表示进程地址空间,VMA的大小正好是一个页面的大小,分别有3个页面映射这3个VMA。这3个页面准备通过KSM来扫描和合并,这3个页面的内容是相同的。具体步骤如下。

  • 3个页面会被添加到KSM中,第一轮扫描中分别给这3个页面分配 rmap_item数据结构来描述它们,并且分别给它计算校验和,如图(a)所示
  • 第二轮扫描中,先扫描page0,若当前稳定的红黑树没有成员,那么不能比较和加入稳定的红黑树。接着,第二次计算校验值,如果 page0的校验值没有发生变化,那么把page0的rmap_item()添加到不稳定的红黑树中,如图(b)所示。如果此时校验值发生了变化,说明页面内容发生变化,这种页面不适合添加到不稳定的红黑树中
  • 扫描 page1,当前稳定的红黑树中没有成员,略过稳定的红黑树的搜索。搜索不稳定的红黑树,遍历红黑树中所有成员。 page1发现自己的内容与不稳定的红黑树中的 rmap_item()一致,因此尝试将page0和 page1合并成一个稳定的节点,合并过程就是让WMA0对映的虚拟地址、vaddr0映时到page1上。,并且把对应的PTE属性修改成只读展性。另外,VMA1映射到 page1的PTE属性也设置为只读属性。新创建一个稳定的节点,这个节点包含了page1的页帧号等信息,把这个稳定的节点添加到稳定的红黑树中。把代表page0的 map _item0和page1的rmap_item1添加到这个稳定的节点的hlist链表中,最后释放page0页面,如图(c)所示
  • 扫描page2。因为稳定的红黑树中有成员,因此,先和稳定的红黑树中的成员进行比较,检査是否可以合并。若发现page2的内容和稳定的节点内容一致,那么把VMA2中的vaddr2映射到稳定的节点对应的 page1上,并且把PTE属性设置为只读属性。把代表page2的rmap_item2添加到稳定的节点的 hlist 链表中,最后释放page2页面,如图(d)所示

KSM的使用的更多相关文章

  1. 虚拟机NUMA和内存KSM

    KSM技术可以合并相同的内存页,即使是不同的NUMA节点,如果需要关闭跨NUMA节点的内存合并,设置/sys/kernel/mm/ksm/merge_across_nodes参数为0.或者可以关闭特定 ...

  2. KSM剖析——Linux 内核中的内存去耦合

    简介: 作为一个系统管理程序(hypervisor),Linux® 有几个创新,2.6.32 内核中一个有趣的变化是 KSM(Kernel Samepage Merging)  允许这个系统管理程序通 ...

  3. Linux内存管理 (17)KSM

    专题:Linux内存管理专题 关键词:KSM.匿名页面.COW.madvise .MERGEABLE.UNMERGEABLE. KSM是Kernel Samepage Merging的意思,用于合并内 ...

  4. linux下的KSM内存共享机制分析

    2017-04-26 KSM是内核中的一种内存共享机制,在2.6.36版本的内核中开始引入,简单来说就是其会 合并某些相同的页面以减少页面冗余.在内核中有一个KSM守护进程 ksmd,它定期扫描用户向 ...

  5. P1306 斐波那契公约数(ksm+结论)

    题目描述 对于Fibonacci数列:1,1,2,3,5,8,13......大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少? Update:加入了一组数据. 输 ...

  6. KSM概念学习

    KSM: Kernel SamePage Merging 内核同页合并 简介 KSM允许内核在两个或多个进程(包括虚拟客户机)之间共享完全相同的内存页. KSM让内核扫描检查正在运行中的程序,并比较他 ...

  7. BZOJ3160万径人踪灭

    Description Input & Output & Sample Input & Sample Output HINT 题解: 题意即求不连续但间隔长度对称的回文串个数. ...

  8. lucene+IKAnalyzer实现中文纯文本检索系统

    首先IntelliJ IDEA中搭建Maven项目(web):spring+SpringMVC+Lucene+IKAnalyzer spring+SpringMVC搭建项目可以参考我的博客 整合Luc ...

  9. 利用Bootstrap快速搭建个人响应式主页(附演示+源码)

    1.前言 我们每个程序员都渴望搭建自己的技术博客平台与他人进行交流分享,但使用别人的博客模板没有创意.做网站后台的开发人员可能了解前端,可是自己写一个不错的前端还是很费事的.幸好我们有Bootstra ...

  10. java web学习总结(二十一) -------------------模拟Servlet3.0使用注解的方式配置Servlet

    一.Servlet的传统配置方式 在JavaWeb开发中, 每次编写一个Servlet都需要在web.xml文件中进行配置,如下所示: 1 <servlet> 2 <servlet- ...

随机推荐

  1. nacos一个奇怪的问题:找不到正确的应用名-环境.后缀名配置文件

    spring: profiles: active: dev application: name: system-api cloud: nacos: server-addr: 192.168.101.6 ...

  2. ComfyUI插件:ComfyUI Impact 节点(三)

    前言: 学习ComfyUI是一场持久战,而 ComfyUI Impact 是一个庞大的模块节点库,内置许多非常实用且强大的功能节点 ,例如检测器.细节强化器.预览桥.通配符.Hook.图片发送器.图片 ...

  3. PHP数组遍历的四种方法

    PHP数组循环遍历的四种方式   [(重点)数组循环遍历的四种方式] 1,https://www.cnblogs.com/waj6511988/p/6927208.html 2,https://www ...

  4. 【Zookeeper】Re03 集群搭建

    我这里采用的是模拟真实情况: 部署三台虚拟机Centos7 192.168.242.101 192.168.242.102 192.168.242.103 每台机器都安装Zookeeper且一致: / ...

  5. 【Java】Collection 集合框架概述

    Collection 集合框架概述 1.集合.数组都是为了存储数据而产生的 2.为什么需要集合?为了更灵活方便的存储数据,且集合能存储的容量比数组更大 3.存储的概念还停留在内存活动范围内,也只是短暂 ...

  6. Linux磁盘/硬盘测速,dd命令

    参考: https://blog.csdn.net/Franciz777/article/details/126779259 ===================================== ...

  7. tensorflow1.x——如何在python多线程中调用同一个session会话

    如何在python多线程中调用同一个session会话? 这个问题源于我在看的一个强化学习代码: https://gitee.com/devilmaycry812839668/scalable_age ...

  8. Win32 菜单添加复选标记和单选标记

    在菜单中添加复选标记用CheckMenuItem函数 CheckMenuItem函数原型: DWORD WINAPI CheckMenuItem( _In_ HMENU hMenu, //菜单句柄 _ ...

  9. 零基础学习人工智能—Python—Pytorch学习(八)

    前言 本文介绍卷积神经网络的上半部分. 其实,学习还是需要老师的,因为我自己写文章的时候,就会想当然,比如下面的滑动窗口,我就会想当然的认为所有人都能理解,而实际上,我们在学习的过程中之所以卡顿的点多 ...

  10. PyJWT 和 python-jose 在处理JWT令牌处理的时候的差异和具体使用

    PyJWT 和 python-jose 是两个用于处理 JSON Web Tokens (JWT) 的 Python 库.它们都有助于生成.解码.验证和管理 JWT,但它们在功能范围和设计哲学上有一些 ...