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

    文章

    10446

    浏览量

    206571
  • 内存
    +关注

    关注

    8

    文章

    2767

    浏览量

    72772
  • CMA
    CMA
    +关注

    关注

    0

    文章

    24

    浏览量

    9732
  • 虚拟内存
    +关注

    关注

    0

    文章

    70

    浏览量

    8018

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

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

收藏 人收藏

    评论

    相关推荐

    Linux内存管理之页面回收

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

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

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

    Linux内核内存泄漏怎么办

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

    Linux内核内存管理架构解析

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

    Linux内核内存管理之内核非连续物理内存分配

    的主要优点是避免了外部碎片,而缺点是需要修改内核页表。显然,非连续内存区域的大小必须是4096的倍数。Linux使用非连续物理内存区的场景有几种:(1)为swap区分配数据结构;(2)
    的头像 发表于 02-23 09:44 380次阅读
    Linux<b class='flag-5'>内核</b><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

    新能力让数据多端协同更便捷,数据跨端迁移更高效!

    “一份”数据的开发体验的目标,在不断增强和提升HarmonyOS的分布式数据管理能力。本期我们就来为大家详细介绍分布式数据管理的新能力。一、分布式数据管理平台面临的挑战我们先来看一个典型的分布式场景:手机
    发表于 01-11 10:41

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

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

    为什么需要CMA?CMA具体是如何工作的

    ,我们需要先了解一点有关迁移类型和 pageblock 的知识。在 buddy 系统中管理着多种迁移类型(migrate type)的内存页面,不同的
    发表于 03-23 11:22

    LINUX内核中的内存是如何进行分配的

    1、devm_kzalloc & devm_kfree函数devm_kzalloc和kzalloc一样都是内核内存分配函数,但是devm_kzalloc是跟设备(装置)有关的,当设备
    发表于 11-04 14:46

    LINUX源代码分析-内存管理

    操作系统管理系统所有的物理空间, 现代大多数操作系统都采取多级管理, 即页面级分配与内核内存分配。就LINUX2-2-5 版本而言,页面级的分配是采用Buddy 算法,而
    发表于 12-19 16:38 102次下载
    LINUX源代码分析-<b class='flag-5'>内存</b>管理

    一文解析Linux内存系统

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

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

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

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

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

    Linux内核虚拟内存管理中的mmu_gather操作

    本文讲解Linux内核虚拟内存管理中的mmu_gather操作,看看它是如何保证刷tlb和释放物理页的顺序的,又是如何将更多的页面聚集起来统一释放的。
    的头像 发表于 05-20 14:37 1607次阅读