0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

内存内核中发生页面迁移的典型场景

Linux爱好者 来源:知乎 2023-11-08 12:28 次阅读

1. 概述

页面迁移(page migrate)最早是为NUMA系统提供一种将进程页面迁移到指定内存节点的能力用来提升访问性能。后来在内核中广泛被使用,如内存规整、CMA、内存hotplug等。

页面迁移对上层应用业务来说是不可感知的,因为其迁移的是物理页面,而应用只访问的是虚拟内存。内核迁移完成后,更新修改对应页表指向迁移后的页面即可。当然了这里说的不可感知是指业务不太关注,也不需要做对应修改。实际上有些场景发生页面迁移是业务性能是有影响的,下面会详细描述。

2. 典型场景

我们列举2个内核中发生页面迁移的典型场景。

2.1 NUMA Balancing引起的页面迁移

在典型 NUMA 中,存在多个 node, 本地 CPU 访问本地 node 节点对应的 memory 性能会快一些。

8acebe30-7dea-11ee-939d-92fbcf53809c.png

Linux 的 NUMA 自动均衡机制会尝试将内存迁移到正在访问它的 CPU 节点所在的 node。如下图所示,CPU24 ~ CPU47访问不是本地 node 对应的 memory,性能会比较慢,系统会将其迁移到本地 node 对应的 memory 以提升访问性能。

8af5dbbe-7dea-11ee-939d-92fbcf53809c.png

迁移后如下图:

8b1387d6-7dea-11ee-939d-92fbcf53809c.png

2.2 内存碎片整理

系统使用一段时候后,由于内存碎片的原因,较难满足连续内存需求,如果需要分配连续大块内存,需要进行内存规整以形成大块连续内存,页面迁移是内存碎片整理的基础。

3. 实现分析

3.1 迁移模式

内核中通过接口migrate_pages实现页而迁移, 分为3个模式。

模式 简介 应用场景
MIGRATE_ASYNC 异步迁移,过程中不会发生阻塞 内存分配slowpath
MIGRATE_SYNC_LIGHT 轻度同步迁移,允许大部分的阻塞操作,唯独不允许脏页的回写操作 kcompactd触发的规整
MIGRATE_SYNC 同步迁移,迁移过程会发生阻塞,若需要迁移的某个page正在writeback或被locked会等待它完成 sysfs主动触发的内存规整
MIGRATE_SYNC_NO_COPY 同步迁移,但不等待页面的拷贝过程。页面的拷贝通过回调migratepage(),过程可能会涉及DMA migrate_vma_pages

3.2 实现流程

内核文档有描述这个API是怎么工作的。不过这个描述着实是不太友好, 不容易在脑海形成画面。

8b3ac846-7dea-11ee-939d-92fbcf53809c.png

我们通过结合代码实现,把这个转化为流程图:

8b69551c-7dea-11ee-939d-92fbcf53809c.jpg

总结一下,页面迁移过程本质就是分配一个 new_page, 解除原有 page 映射,把旧 page 复制到新 page 并建立新 page 的映射。

4. 页面迁移过程用户态访问处理

到这里可能会有疑问:如果在页面迁移过程中,应用发生发访问这个迁移中的页面,会发生什么?

情景1: 旧页面的页表还未解映射, 此时发生缺页可以正常访问原来页面。

8b85670c-7dea-11ee-939d-92fbcf53809c.jpg

情景2: 旧页面解除了映射,但新页面还未建立映射。这时访问会发生等待,需要等新页面建立映射并copy完成页面后才能访问。

8b964086-7dea-11ee-939d-92fbcf53809c.jpg

情景3: 完成了页面迁移动作,可以正常访问新页面了。

8baf4310-7dea-11ee-939d-92fbcf53809c.jpg

下面我们重点分析一下,当旧页面解除了映射,且新页面未建立映射这个过程中发生了用户态访问,内核的处理流程是怎样的。

首先我们看一下旧页面解除了映射的过程:

staticbooltry_to_unmap_one(structpage*page,structvm_area_struct*vma,
unsignedlongaddress,void*arg)
{
...
if(PageHWPoison(page)&&!(flags&TTU_IGNORE_HWPOISON)){

...
}elseif(pte_unused(pteval)&&!userfaultfd_armed(vma)){
...
}elseif(IS_ENABLED(CONFIG_MIGRATION)&&
(flags&(TTU_MIGRATION|TTU_SPLIT_FREEZE))){
// 页面迁移会设置TTU_MIGRATION标记,走到这个分支来
swp_entry_tentry;
pte_tswp_pte;

if(arch_unmap_one(mm,vma,address,pteval)< 0) {
    set_pte_at(mm, address, pvmw.pte, pteval);
    ret = false;
    page_vma_mapped_walk_done(&pvmw);
    break;
   }

   /*
    * Store the pfn of the page in a special migration
    * pte. do_swap_page() will wait until the migration
    * pte is removed and then restart fault handling.
    */
    // 迁移中的页面, 生成了一个swap entry, 并写到PTE页表项中
    // 当再次发生缺页时会走进do_swap_page等待直到迁移完成.
   entry = make_migration_entry(subpage, pte_write(pteval));
   swp_pte = swp_entry_to_pte(entry);
   if (pte_soft_dirty(pteval))
    swp_pte = pte_swp_mksoft_dirty(swp_pte);
   if (pte_uffd_wp(pteval))
    swp_pte = pte_swp_mkuffd_wp(swp_pte);
    // 当设置了迁移标记的Swap entry到pte后, 这个旧页面就不能像原来那样的顺利被访问了
   set_pte_at(mm, address, pvmw.pte, swp_pte);
   /*
    * No need to invalidate here it will synchronize on
    * against the special swap migration pte.
    */
  } else if (PageAnon(page)) {
   swp_entry_t entry = { .val = page_private(subpage) };
   pte_t swp_pte;
   /*
    * Store the swap location in the pte.
    * See handle_pte_fault() ...
    */
   if (unlikely(PageSwapBacked(page) != PageSwapCache(page))) {
    WARN_ON_ONCE(1);
    ret = false;
    /* We have to invalidate as we cleared the pte */
    mmu_notifier_invalidate_range(mm, address, address + PAGE_SIZE);
    page_vma_mapped_walk_done(&pvmw);
    break;
   }
...
}

解除映射后,再次发生映射就走到do_swap_page中了。

vm_fault_tdo_swap_page(structvm_fault*vmf)
{
...
//获取到这是一个在迁移过程的的PTE的标识
entry=pte_to_swp_entry(vmf->orig_pte);
if(unlikely(non_swap_entry(entry))){//不是传统的Swapentry
if(is_migration_entry(entry)){//是迁移标记进来的
/*等待migration的完成。本质是在等待旧page释放其page lock
*最终调用到wait_on_page_bit_common
*/
migration_entry_wait(vma->vm_mm,vmf->pmd,vmf->address);
}
...
}

总结一下:

页面迁移前,首先会获取旧页面和新页面的页面锁PG_lock,在解除映射的时候传入了由于页面迁移导致的解映射标记TTU_MIGRATION,设置了此标记会生成一个带页面迁移标识的swap_entry设置到pte中。在设置好的那一刻走,应用进程无法很顺利地访问这个页面了,需要通过do_swap_entry路径。

假如此时应用进程访问了这个页面,会走进到do_swap_entry,取出带迁移标识的swap_entry,识别到这个标识,会等待页面锁释放。页面锁只有在页面迁移完成后才会被释放,也就是会发生等待直到页面迁移完成。

5. 用户态如何避免发生页面迁移

上面我们已经知道,如果有页面迁移过程中发生用户态访问,很可能是需要发生等待其迁移完成, 这个过程需要一定耗时。而有时的场景我们是需要避免此种时延抖动,那有什么办法呢?

方法就是让这个页面短时间内变得不可移动。

intmigrate_page_move_mapping(structaddress_space*mapping,
structpage*newpage,structpage*page,intextra_count)
{
...
if(page_count(page)!=expected_count)
return-EAGAIN;
...
returnMIGRATEPAGE_SUCCESS;
}

可以看到当发生页面复制过程中,如果 page 的引用计数不符合预期(期望为0)时,这时系统认为有人在使用,不适用做迁移。那么,我们只需要增加 page 的引用计数就可以。

可以在不想被迁移的时间段开始前通过pin_user_pages这样的接口,结束时unpin就可以了。接口最终会调到try_grab_page增加引用计数。

bool__must_checktry_grab_page(structpage*page,unsignedintflags)
{
...
refs=GUP_PIN_COUNTING_BIAS;//#defineGUP_PIN_COUNTING_BIAS(1U<< 10)
   page_ref_add(page, refs);
  }

  return true;
}

编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10850

    浏览量

    211517
  • 内存
    +关注

    关注

    8

    文章

    3016

    浏览量

    73989
  • CMA
    CMA
    +关注

    关注

    0

    文章

    27

    浏览量

    9804
  • 虚拟内存
    +关注

    关注

    0

    文章

    77

    浏览量

    8057

原文标题:图解|内存页面迁移技术

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内存管理之页面回收

    请求调页机制,只要用户态进程继续执行,他们就能获得页框,然而,请求调页没有办法强制进程释放不再使用的页框。因此,迟早所有空闲内存将被分配给进程和高速缓存,Linux内核页面回收算法(PFRA)采取从用户进程和
    发表于 05-19 14:09 1086次阅读
    Linux<b class='flag-5'>内存</b>管理之<b class='flag-5'>页面</b>回收

    走进Linux内存系统探寻内存管理的机制和奥秘

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍Linux 内存组织结构和页面布局,
    的头像 发表于 01-05 09:47 1616次阅读

    Linux内核内存泄漏怎么办

    在Linux内核开发中,Kmemleak是一种用于检测内核内存泄漏的工具。
    发表于 07-04 11:04 819次阅读

    Linux内存相关知识科普

    ,Linux 内核几种内存管理的方法,内存使用场景以及内存使用的那些坑。**从内存的原理和结构,
    发表于 07-25 14:43 723次阅读
    Linux<b class='flag-5'>内存</b>相关知识科普

    Linux内核内存规整总结

    1.前言 伙伴系统作为内核最基础的物理页内存分配器,具有高效、实现逻辑简介等优点,其原理页也尽可能降低内存外部碎片产生,但依然无法杜绝碎片问题。外部碎片带来的最大影响就是内存足够,但是
    的头像 发表于 11-11 11:17 1305次阅读
    Linux<b class='flag-5'>内核</b><b class='flag-5'>内存</b>规整总结

    Linux内核内存管理架构解析

    内存管理子系统可能是linux内核中最为复杂的一个子系统,其支持的功能需求众多,如页面映射、页面分配、页面回收、
    的头像 发表于 01-04 09:24 650次阅读
    Linux<b class='flag-5'>内核</b><b class='flag-5'>内存</b>管理架构解析

    鸿蒙OS开发:【一次开发,多端部署】(典型布局场景

    虽然不同应用的页面千变万化,但对其进行拆分和分析,页面中的很多布局场景是相似的。本小节将介绍如何借助自适应布局、响应式布局以及常见的容器类组件,实现应用中的典型布局
    的头像 发表于 05-25 16:39 2132次阅读
    鸿蒙OS开发:【一次开发,多端部署】(<b class='flag-5'>典型</b>布局<b class='flag-5'>场景</b>)

    鸿蒙OS开发:典型页面场景【一次开发,多端部署】(设置应用页面

    本小节以“设置”应用页面为例,介绍如何使用自适应布局能力和响应式布局能力适配不同尺寸窗口。
    的头像 发表于 05-27 10:33 1154次阅读
    鸿蒙OS开发:<b class='flag-5'>典型</b><b class='flag-5'>页面</b><b class='flag-5'>场景</b>【一次开发,多端部署】(设置应用<b class='flag-5'>页面</b>)

    鸿蒙OS开发:典型页面场景【一次开发,多端部署】实战(设置典型页面

    本示例展示了设置应用的典型页面,其在小窗口和大窗口有不同的显示效果,体现一次开发、多端部署的能力。
    的头像 发表于 05-27 09:36 1133次阅读
    鸿蒙OS开发:<b class='flag-5'>典型</b><b class='flag-5'>页面</b><b class='flag-5'>场景</b>【一次开发,多端部署】实战(设置<b class='flag-5'>典型</b><b class='flag-5'>页面</b>)

    鸿蒙OS开发:典型页面场景【一次开发,多端部署】(短信)案例介绍

    本章从系统预置的应用中,选择短信应用作为典型的案例,从页面开发和工程结构的角度,介绍"一多"的具体实践。系统的产品形态在不断丰富中,当前主要有默认设备和平板两种产品形态,本章的具体实践也将围绕这两种产品形态展开。
    的头像 发表于 05-28 15:08 1264次阅读
    鸿蒙OS开发:<b class='flag-5'>典型</b><b class='flag-5'>页面</b><b class='flag-5'>场景</b>【一次开发,多端部署】(短信)案例介绍

    Linux内存系统:内存使用场景

    out of memory 的时代过去了吗?no,内存再充足也不可任性使用。1、内存的使用场景· page 管理· slab(kmalloc、内存池)· 用户态
    发表于 08-25 07:42

    内存之旅——如何提升CMA利用率?

    (migrate type)的内存页面,不同的迁移类型有各自的用途。举例来说, MIGRATE_MOVABLE 表示保存在其页面的数据是可以被迁移
    发表于 03-22 16:26

    一文解析Linux内存系统

    Linux 内存是后台开发人员,需要深入了解的计算机资源。合理的使用内存,有助于提升机器的性能和稳定性。本文主要介绍Linux 内存组织结构和页面布局,
    的头像 发表于 09-01 10:46 2436次阅读
    一文解析Linux<b class='flag-5'>内存</b>系统

    基于内存关联分析的内存预拷贝迁移策略

    内存预拷贝迁移在密集型负载下存在内存脏页反复传输的冋题,导致迭代轮数较多且大幅降低了内存预拷贝迁移的整体性能。脏页概率预测能够有效减少
    发表于 05-24 15:40 14次下载

    Linux内核中的页面分配机制

    Linux内核中是如何分配出页面的,如果我们站在CPU的角度去看这个问题,CPU能分配出来的页面是以物理页面为单位的。也就是我们计算机中常讲的分页机制。本文就看下Linux
    的头像 发表于 08-07 15:51 272次阅读
    Linux<b class='flag-5'>内核</b>中的<b class='flag-5'>页面</b>分配机制