linux ksm实现与代码简述

news2024/12/26 11:12:04

KSM 全称是 Kernel Samepage Merging,表示相同的物理页只映射一份拷贝。

原理

在ksm初始化时(ksm_init),注册了一个ksm_scan_thread线程,这个线程的核心入口是ksm_do_scan。当对一个进程第一次通过madvice(MADV_MERGEABLE)标记一段内存可合并时,会触发__ksm_enter将当前进程标记为MMF_VM_MERGEABLE,并把进程的mm_struct放在ksm_mm_head链表上。ksm_scan_thread会在ksm_mm_head链表上做扫描,找到标记合并的匿名页中,page内容的checksum不变的页(说明最近没有写入),如果是将找到的mergable 页合并到stable_tree 的 node上,将相应pte置为同一个物理地址。当有写操作时,会因为write_protected标记触发cow机制,生成新的页,并从stable tree里移除。

实现

ksm_scan_thread线程的核心是 ksm_do_scan,它会扫描所有进程(ksm_mm_head链表上的所有进程)的可合并 vma,找到checksum不变的页,如果stable tree没有,就添加到page所在numa 节点的unstable tree上,如果原本unstable tree上有,就一起移至stable tree(即引用超过2页的mergable 页才会移至stable tree中)。一轮扫描结束后,unstable tree会被清空,并在下轮扫描中重建。

每个numa 节点都有一个 stable tree和unstable tree。如果开启了ksm_merge_across_nodes,则所有numa node共用0号节点。

stable tree 是一个红黑树,当共享页的vma很多,超过ksm_max_page_sharing(256)个时,会将stable tree 的node 转为chain list node,每个chain list node 上最多存256个vma节点。它们指向同一个物理页。

unstable tree也是一个红黑树,一轮扫描结束后,unstable tree会被清空,并在下轮扫描中重建。

代码简述

ksm_do_scan主要由scan_get_next_rmap_item找可合并匿名页,由cmp_and_merge_page到对应numa节点找页尝试合并

scan_get_next_rmap_item

找mergable 的mapping了物理页的page。最终会连成一个链存在mm_struct->rmap_list上。

会顺带将不再mapping或设置unmergable的rmap_item删掉。

scan_get_next_rmap_item():
  // 新一轮扫描
  if (mm_slot == &ksm_mm_head) {
    /**
     * 新一轮扫描前首先触发一次lru_add_drain_all
     * 因为lru如果一直不刷新的话,有些无用的页会因为引用计数而不能做merge。
     */
    lru_add_drain_all();

    /**
   * 如果一页做了迁移,在一轮结束时应该已经有对应节点加在了正确numa节点的stable tree上
     * 并增加了ref,可以安全地将ref减1了
     */
    if (ksm_merge_across_nodes) {
      list_for_each_entry_safe(stable_node, next, &migrate_nodes, list) {
	    page = get_ksm_page(stable_node, GET_KSM_PAGE_NOLOCK);
	    if (page)
		  put_page(page);
	    cond_resched();
      }
    }
    
  }

  vma_iter_init(&vmi, mm, ksm_scan.address);
  for_each_vma(vmi, vma) {
    /**
     * 遍历一个进程所有mergeable anon vma中的有物理页的page,跳过device页,
   * 找到对应 address 的 rmap_item,或为它新创建一个rmap_item,
     * 上一次扫描到的ksm_scan.rmap_list 到它之间的所有item都
     * 不再mergable或没有物理页了,需要删除 rmap_item。
     * (这样一轮下来,整个进程的全部mergable的物理页的rmap,
     *   就全放在mm_struct->rmap_list上了)
     */
   rmap_item = get_next_rmap_item(mm_slot, ksm_scan.rmap_list, ksm_scan.address);
     return rmap_item; // 找到了一个mergable匿名页
  }
  // 如果扫描一轮发现这个进程没有ksm页了,就删掉对应mm_slot
  hash_del(&mm_slot->slot.hash);
  list_del(&mm_slot->slot.mm_node);
  

cmp_and_merge_page

如果目标页的ksm页大于等于2个,则能找目标页所在numa节点上的stable node,加入上去。

在还没找到时,会先把自己加在unstable tree对应numa节点上,等后面的ksm页发现自己,并与自己一同加到stable tree上。

如果一个numa节点的stable tree上的一个ksm页,有多个dup节点,它们会连成一个chain,在stable_tree_search->chain_prune时会优先找到映射最多page结构的dup节点,与它合并。

搜索可合并stable node过程中会顺带发现不属于当前numa 节点的ksm页,从树上删除,并在之后整一轮扫描结束时,将ref减1。

cmp_and_merge_page():
  stable_node = page_stable_node(page);
  if (stable_node) {
    如果不支持ksm迁移,且物理页做了 numa node 迁移。
    则把 stable node 迁移至migrate_nodes上。
    否则它已经在 stable 树上了,直接返回
  }

 // 找出一个 ksm 页
  kpage = stable_tree_search();

 // 如果有这样的 ksm 页,则将此页的pte映射到ksm页上去。并插入 stable tree。
  if (kpage) {
    try_to_merge_with_ksm_page();
    stable_tree_append(rmap_item, page_stable_node(kpage));
  }

  // 还没有这样的 ksm 页,计算 checksum ,看是不是与上次一样,一样则认为没有修改
  calc_checksum(page);

 // 如果checksum变了, 它可能被频繁修改,不对这样的页做合并
  if (rmap_item->oldchecksum != checksum) {
    rmap_item->oldchecksum = checksum;
	return;
  }

  // 如果checksum是0页,则与0页合并(0页是刚初始化的页)
  try_to_merge_one_page(vma, page, ZERO_PAGE(rmap_item->address));

  // 尝试从本轮的对应 numa 节点的 unstable tree 上找有没有出现过相同内容页,
 // 没有则插入 unstable tree
  unstable_tree_search_insert()

  // 如果unstable tree有,说明有两个同样内容的页内容一直没变,可以合并到 stable tree
  try_to_merge_two_pages()
  stable_tree_append(tree_rmap_item, stable_node);
  stable_tree_append(rmap_item, stable_node);
 
  // 如果两个相同内容页出现在同一个 compound page 上
  // 则只是拆分复合页,先不合并ksm,因为需要重新拿锁,可以等到下一轮
  split_huge_page(); 

stable_tree_search

搜索过程中,如果自己的page已经是migrate stable node了,就可以找个树上的节点替换,并返回自己。

在stable tree搜索过程中,会顺便发现物理页已经迁移了的node,并将其从树上移除。

stable_tree_search():
  // 如果页对应的stable node存在,则前面的cmp_and_merge_page
  // 保证了它在migrate_nodes上
  page_node = page_stable_node(page);

 // 在对应numa节点的红黑树查找
  nid = get_kpfn_nid(page_to_pfn(page));
  root = root_stable_tree + nid;
  new = &root->rb_node;
  while (*new) /* 一层层找到叶子节点 */{
    // 红黑树的节点可能是一个dup节点,如果vma超过了256,节点会组成dup链chain
   // 如果超过一定时间,则红黑树上chain节点的dup链,看是否只有一个dup节了。
   // 如果是,则用dup节点代替红黑树上的chain节点。
    // 这同时,会尝试把最多vma的dup节点放在chain的头上,作为下次合并首选dup
    chain_prune();
 
   // 比较页的内容,从而在红黑树上向下找
    ret = memcmp_pages(page, tree_page);
 
    // 如果目标页有stable node节点,且是一个物理页迁移了的 stable 节点
    if (page_node) {
       // 修改它的nid为它迁移到的numa节点id,并加回 stable tree 
       //(mapcount > 1 时,以dup形式加到node chain上,等于1时走if后的逻辑加到dup上)
       DO_NUMA(page_node->nid = nid);
       stable_node_chain_add_dup(page_node, stable_node)
    }

    // chain_prune已经取了最多map页的dup节点
    // 这里判断下如果numa id不变,说明没有迁移过,可直接返回
    tree_page = get_ksm_page(stable_node_dup);
    if (get_kpfn_nid(stable_node_dup->kpfn) == NUMA(stable_node_dup->nid)) {
      return tree_page;
    }
    /**
     * 如果numa id 变过,则刚好发现了一个 numa 节点迁移了的 node
     * 可顺便将其从树上删除。并尝试将原page的stable migrate node 加回树上
     * (调用它的 cmp_and_merge_page 保证了如果page有stable node对应,则一定是migrate node)
     */
    if (dup节点在红黑树上的chain上) {

      // 可直接将原节点删掉
      __stable_node_dup_del(stable_node_dup);
      if (page没有对应stable node migrate 节点))
        return null;

      // 如果page有节点,就把page改numa id后加到chain上去
      DO_NUMA(page_node->nid = nid);
      stable_node_chain_add_dup(page_node, stable_node);
      return page;

    } else /* dup 节点直接在红黑树上 */{
      if (page有对应stable node migrate 节点) {

        // 直接交换,并返回原页(因为它已经在树上了)
        rb_replace_node(&stable_node_dup->node, &page_node->node, root);
     return page;
      } else /* page 没有 stable node节点对应 */ {

        // 移除原节点,返回null(因为它没在树上了)
        rb_erase(&stable_node_dup->node, root);
        return null;
      }
    }
  }
   
    

页回收

当页被回收时,物理页的flag上swapcache标记会清理,导致get_ksm_page中观察到这个现象,并触发stable node 的删除,下次触发缺页时每个进程的页需要重新建立页的pte,再由ksmd线程重新扫描发现可合并的页。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1305030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年度盘点:AIGC、AGI、GhatGPT、人工智能大模型必读书单

2023年是人工智能大语言模型大爆发的一年,一些概念和英文缩写也在这一年里集中出现,很容易混淆,甚至把人搞懵。 LLM:Large Language Model,即大语言模型,旨在理解和生成人类语言。LLM的特点是规模庞大&…

代码随想录算法训练营 | day49 动态规划 121.买卖股票的最佳时机,122.买卖股票的最佳时机Ⅱ

刷题 121.买卖股票的最佳时机 题目链接 | 文章讲解 | 视频讲解 题目:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设…

【详细解答】项目经VS产品经理,有什么区别?哪个更值得选择?

最近很多人咨询“项目经理跟产品经理该怎么选,我更适合哪个?”“项目经理跟产品经理哪个更有钱途 ”“项目经理转产品经理好转吗”等等,今天就一次性说清楚项目经理跟产品经理有什么区别,应该怎么选择。 不想看长篇大论的&#x…

Python 递归、闭包与装饰器的编程魔法

更多资料获取 📚 个人网站:ipengtao.com 在Python编程中,递归、闭包和装饰器是一些强大的工具,它们能够为代码增色不少,提高代码的可读性和灵活性。本文将深入探讨这三种编程魔法的原理和应用,通过丰富的示…

JavaScript实现飘窗功能

实现飘窗功能很简单 html代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title…

Certbot实现 HTTPS 自动续期

Certbot实现 HTTPS 自动续期 以前阿里云支持申请一年的免费https证书&#xff0c;那每年我们手动更新证书并没什么大问题&#xff0c;但现在阿里云的免费证书仅支持3个月&#xff0c;这意味着每三个月都要要申请一下证书显得非常麻烦。 下面我们使用Certbot实现ssl证书的自动…

记录hive/spark取最新且不为null的方法

听标题可能听不懂我想表达的意思&#xff0c;我来描述一下我要做的事&#xff1a; 比如采集同学对某一网站进行数据采集&#xff0c;同一个用户每天会有很多条记录&#xff0c;所以我们要取一条这个用户最新的状态&#xff0c;比如用户改了N次昵称&#xff0c;我们只想得到最后…

kubeadm搭建单master多node的k8s集群--小白文,图文教程

参考文献 K8S基础知识与集群搭建 kubeadm搭建单master多node的k8s集群—主要参考这个博客&#xff0c;但是有坑&#xff0c;故贴出我自己的过程&#xff0c;坑会少很多 注意&#xff1a; 集群配置是&#xff1a;一台master&#xff1a;zabbixagent-k8smaster&#xff0c;两台…

C语言leetcode集训一:数组

为了进一步巩固C语言基础&#xff0c;同时进一步了解leetcode刷题的流程&#xff0c;开始进行C语言的集训&#xff0c;今天是第一天&#xff0c;看看我都做了哪些题&#xff0c;因为周末&#xff0c;有点颓废&#xff0c;所以基本上都是简单题&#xff0c;现在只想睡觉...... 有…

ultralytics yolo图像分类训练案例;pytorch自有数据集图像分类案例

1、ultralytics yolo图像分类训练案例 优点:使用方便,训练过程评估指标可以方便查看 缺点:自带模型少,可选择自定义小 参考:https://docs.ultralytics.com/tasks/classify/#val https://blog.csdn.net/weixin_42357472/article/details/131412851 1)数据集格式 https://…

一文让你认识什么是springboot(创建第一个 SpringBoot 程序)

文章目录 前言1. 环境准备2. Maven3. 第一个 SpringBoot 程序3.1 SpringBoot 介绍3.2 SpringBoot 项⽬创建3.2.1 使用 IDEA 创建3.2.2 网页版创建&#xff08;了解&#xff09; 3.3 项目代码和目录介绍3.3.1 观察pom⽂件3.3.2 父工程3.3.3 目录介绍 3.4 运行项目3.5 输出 Hello…

STM32——串口

串口发送/接收函数&#xff1a; HAL_UART_Transmit(); 串口发送数据&#xff0c;使用超时管理机制 HAL_UART_Receive(); 串口接收数据&#xff0c;使用超时管理机制 HAL_UART_Transmit_IT(); 串口中断模式发送 HAL_UART_Receive_IT(); 串口中断模式接收 HAL_UART_Tran…

线上业务优化之案例实战

本文是我从业多年开发生涯中针对线上业务的处理经验总结而来&#xff0c;这些业务或多或少相信大家都遇到过&#xff0c;因此在这里分享给大家&#xff0c;大家也可以看看是不是遇到过类似场景。本文大纲如下&#xff0c; 后台上传文件 线上后台项目有一个消息推送的功能&#…

thinkphp 使用array_reduce 处理返回的数据格式

我想要的效果&#xff1a; 不使用array_reduce 的效果 &#xff1a; 代码&#xff1a; public function teamList($userId,$good_id){$nowbuyers $this->order->where(good_id,$good_id)->count();$data GroupTotalOrder::alias(t_order)->where(merchant_Id,$u…

每日一题 2454. 下一个更大元素 IV(困难,单调栈)

首先考虑第一大整数问题维护一个单调栈&#xff0c;遍历 nums&#xff0c;弹出栈中所有小于 nums[i] 的数&#xff0c;而 nums[i] 就是这些被弹出的数的第一大整数&#xff0c;知道栈为空或者栈顶元素比 nums[i] 大&#xff0c;证明如下&#xff0c;首先由于是遍历&#xff0c;…

Knowledge Graph知识图谱—9. Knowledge Modeling

9. Knowledge Modeling & Ontology Engineering How should the knowledge in a KG be modeled? – Which classes of entities do we have? – Which relations connect them? – Which constraints hold for them? → these questions are defined in the ontology …

【智能家居】九、停车场车牌识别功能点(回调、解耦)

一、翔云 人工智能开放平台&#xff08;车牌识别&#xff09; 二、cJSON 库 三、实现代码 四、回调函数 五、人脸识别和车牌识别获取数据的区别 六、异步网络请求和同步网络请求的区别 七、解耦 一、翔云 人工智能开放平台&#xff08;车牌识别&#xff09; 翔云 人工智能开放…

vxe-table 右键菜单+权限控制(v3)

1.menu-config 是用于配置右键菜单的属性。通过 menu-config 属性&#xff0c;定义右键菜单的内容、显示方式和样式。 通过 menu-config 属性配置了右键菜单&#xff0c;其中的 options 属性定义了右键菜单的选项。用户在表格中右键点击时&#xff0c;将会弹出包含这些选项的自…

数据库系统原理与实践 笔记 #12

文章目录 数据库系统原理与实践 笔记 #12事务管理和并发控制与恢复(续)并发控制SQL-92中的并发级别基于锁的协议基于锁的协议的隐患锁的授予封锁协议两阶段封锁协议多粒度粒度层次的例子意向锁类型相容性矩阵多粒度封锁模式基于时间戳的协议基于时间戳协议的正确性基于有效性检…

mysql:用SHOW CREATE TABLE tbl_name查看创建表的SQL语句

https://dev.mysql.com/doc/refman/8.2/en/show-create-table.html 可以用SHOW CREATE TABLE tbl_name查看创建表的SQL语句。 例如&#xff0c;SHOW CREATE TABLE test_table;表示查询创建test_table表的SQL语句&#xff1a;