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

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

3天内不再提示

如何避免Linux的物理内存碎片化

Linux阅码场 来源:未知 作者:伍文辉 2018-05-01 16:43 次阅读

Linux buddyy系统是linux kernel比较稳定的一个模块,但是并不是说它没有缺陷,Linux内存管理系统自诞生之日,就一直存在物理内存碎片化的问题:在系统启动并且运行很长一段时间后,极端情况下,尽管总的可用物理page数目很高,但是空闲的连续物理内存可能并不大,这就造成申请大块连续物理内存分配时失败。尤其是当分配操作带有ATOMAIC标记时,系统连回收内存的机会都没有。

避免碎片

很长时间以来,物理内存的碎片化一直是Linux操作系统的弱点之一,尽管已经有人提出了很多解决方法,但是没有哪个方法能够彻底的解决,memory buddy分配就是解决方法之一。 我们知道磁盘文件也有碎片化问题,但是磁盘文件的碎片化只会减慢系统的读写速度,并不会导致功能性错误,而且我们还可以在不影响磁盘功能的前提的下,进行磁盘碎片整理。而物理内存碎片则截然不同,物理内存和操作系统结合的太过于紧密,以至于我们很难在运行时,进行物理内存的搬移(这一点上,磁盘碎片要容易的多;实际上mel gorman已经提交了内存紧缩的patch,只是还没有被主线内核接收)。 因此解决的方向主要放在预防碎片上。

在2.6.24内核开发期间,防止碎片的内核功能加入了主线内核。在了解反碎片的基本原理前,先对内存页面做个归类:

1. 不可移动页面 unmoveable:在内存中位置必须固定,无法移动到其他地方,核心内核分配的大部分页面都属于这一类。

2. 可回收页面 reclaimable:不能直接移动,但是可以回收,因为还可以从某些源重建页面,比如映射文件的数据属于这种类别,kswapd会按照一定的规则,周期性的回收这类页面。

3. 可移动页面 movable:可以随意的移动。属于用户空间应用程序的页属于此类页面,它们是通过页表映射的,因此我们只需要更新页表项,并把数据复制到新位置就可以了,当然要注意,一个页面可能被多个进程共享,对应着多个页表项。

防止碎片的方法就是把这三类page放在不同的链表上,避免不同类型页面相互干扰。考虑这样的情形,一个不可移动的页面位于可移动页面中间,那么我们移动或者回收这些页面后,这个不可移动的页面阻碍着我们获得更大的连续物理空闲空间。

(图片来源:https://images2015.cnblogs.com/blog/758933/201703/758933-20170301165125907-1024080699.png)

针对页面的分类,我们引入了movable zone,事实上movable zone是虚拟zone,是在运行时逐渐建立的。当然内核的确可以建立真实的内存zone。

我们知道大部分buddy分配失败,发生在申请unremovable页面时。这样分类还有一个潜在的好处,为unremovable保留的页面,被reclaimable和movable分配的优先级低(参见fallbacks),因此客观上减少了buddy分配unremovable页面的几率。

数据结构

kernel引入了一些宏来表示不同的迁移类型:

#define MIGRATE_UNMOVABLE 0#define MIGRATE_RECLAIMABLE 0#define MIGRATE_MOVALBE 0#define MIGRATE_RESERVE 0#define MIGRATE_ISOLATE 0#define MIGRATE_TYPES 0

类型MIGRATE_UNMOVABLE, MIGRATE_RECLAIMABLE和MIGRATE_MOVALBE就是我们上面介绍的三种页面类型。如果向特定类型页面分配请求失败,紧急情况下可以从MIGRATE_RESERVE分配内存。

当制定类型的空闲列表无法满足分配时,可以按照一定规则从其他类型空闲链表分配,这个次序用下面数据描述

static int fallbacks[MIRGRATE_TYPES][MIGRATE_TYPES-1] = { [MIGRATE_UNMOVABLE] = {MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE}, [MIGRATE_RECLAIMABLE] = {MIRGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE}, [MIGRATE_MOVABLE] = {MIRGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE}, [MIGRATE_RESERVE] = {MIGRATE_RESERVE, MIGRATE_RESERVE, MIGRATE_RESERVE},}

和zone_list功能类似,当内核想要分配不可移动页面,如果该链表为空,则优先选择从RECLAIMABLE链表分配,然后是MOVABLE,最后使用RESERVE链表。

实际上,这种方法并不能解决我们的问题,因为用户空间页面映射以及内核申请RECLAIMABLE页面的需求可能是无止境的,当MOVABLE和RECLAIMABLE链表无法满足分配时,根据fallbacks会占用MIGRATE_UNMOVABLE链表,这就导致后面UNMOVABLE分配可能失败。

So,我们可以修改fallbacks如下

static int fallbacks[MIRGRATE_TYPES][MIGRATE_TYPES-1] = { [MIGRATE_UNMOVABLE] = {MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE}, [MIGRATE_RECLAIMABLE] = {MIRGRATE_MOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE}, [MIGRATE_MOVABLE] = {MIRGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE}, [MIGRATE_RESERVE] = {MIGRATE_RESERVE, MIGRATE_RESERVE, MIGRATE_RESERVE},}

禁止MOVABLE或者RECLAIMABLE失败后尝试从UNMOVABLE链表分配页面,这样可以保持UNMOVABLE不受非关键页面分配的干扰。

注意:即便可移动分组特性已经编译到内核中,但是只有当系统中有足够内存可以分配给多个类型的链表时,该特性才有意义。这个足够的含义由pageblock_order和pageblock_nr_pages来定义。当可用内存过少的时候,引入页面迁移没有任何好处,相反会增加系统负担。系统会在 build_all_zonelist中进行检查,如果没有足够内存,则关闭该特性。

在内存子系统初始化期间,memmap_init_zone负责处理内存域的page实列,所有的页最初都标记为可移动的!

mm/page_alloc.cvoid __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone, unsigned long start_pfn, enum memmap_context context){ strcut page *page; unsigned long end_pfn = start_pfn+size; unsigned long pfn; for (pfn = start_pfn; pfn < end_pfn; pfn++) {        ...        if ((pfn & (pageblock_nr_pages - 1)))            set_pageblock_migratetype(page, MIGRATE_MOVABLE);      ...}

在进行内存分配时,如果没有预定迁移类型的内存区。那么会尝试从MOVABLE链表上获取尽可能大的内存区,并转换到相应的列表,由于获取的内存区长度是最大的,因此不会向可移动内存区引入碎片。这种做法使得不同类型的页面从不同的页面范围内分配,从而使得不同类型的内存分配比避免干扰。

内存分配器如何知道分配申请是哪种迁移类型呢,这需要所有内存申请提供相应的分配标记,如果需要分配可移动的内存页,那么使用__GFP_MOVABLE,如果申请可回收的则使用__GFP_RECLAIMABLE。如果这些标记都没有设置,则认为是UNMOVABLE的。

Virtual Movable Zone

这种方法是提前分配一个称为ZONE_MOVABLE的内存zone,ZONE_MOVABLE内核特性必须由管理员显示激活。其基本思想是:把物理内存划分为两个内存zone,一个用于可移动分配,另外一个用于不可移动分配。这样不可移动内存域不会干扰可移动内存域引入碎片,而可移动内存域因此很容易满足分配。

当然,如何划分这两个内存域,对系统管理员是个挑战。

和系统中的其他内存域不同,ZONE_MOVABLE是一个虚拟的内存域,它的内存取自高端内存域或者普通内存域。ZONE_MOVABLE内存有两种提取方式:

1. 用于可移动分配的内存平均分布到所有的内存节点上。

2. 只使用来自最高内存域的内存,在内存较多的32位系统上,这通常是ZONE_HIGHMEM。注意这也和ZONE_MOVABLE的使用者吻合,因为只有用户页面映射才算是MOVA LE的内存页,而用户页面映射优先使用的也是HIGHMEM。

因此对于ZONE_MOVABLE我们可以使用如下策略:

1. 使能高端内存

2. ZONE_MOVABLE从HIGHMEM提取内存

3. 系统管理员估算ZONE_MOVABLE的大小,较小的ZONE_MOVABLE使得非movable ZONE有更多地物理内存。

ZONE_MOVABLE 使用者

因为ZONE_MOVABLE的使用者是带有GFP_MOVABLE和GFP_HIGHMEM标记的内核分配(主要是应用程序的页面映射)。

void *vmalloc(unsigned long size){ return __vmalloc_node_flags(size, -1, GFP_KERNEL | __GFP_HIGHMEM);}

结论

ZONE_MOVABLE和页面分类方法相比,好处是很明显的:固定UNMOVABLE zone的大小(页面分类链表是动态生成的),UNMOVABLE zone供内核关键分配函数使用。系统频繁申请的MOVABLE分配,不会导致unmovable zone的碎片化。但是缺点仍然很明显,即RECLAIMABLE分配还是使用unmovable zone,频繁的分配回收仍然使得unmovable zone碎片化。

所以看起来,ZONE_MOVABLE方法只是缓解了物理内存碎片化,但是并没有完全解决。

对于某些特定的驱动,我们可以通过以下方式减少分配失败的可能性。

1. 减少分配所需的连续页面数目。

2. 如果内存申请操作对系统来说是关键操作(比如framebuffer,网络传输buffer),不允许分配失败,但是又无法做到1,那么可以考虑使用预分配的策略。

对于某些特定的项目,可以通过如下方法减少DMA内存分配失败的可能性。

1. 禁止带有GFP_HIGHMEM标记的内存分配在HIGHMEM zone 分配失败后,进入DMA zone寻找合适的页面。

2. 禁止对Normal zone分配失败,进入DMA zone寻找合适页面

HIGHMEM的使用者主要是应用程序的页面映射和内核vmalloc分配,这两种操作都不需要连续物理页面,HIGHMEM zone并不关心物理内存碎片化,而且这两种操作映射的页面本身就是reclaimable,实在没有必要再去占用Normal和DMA zone的物理页面。

尤其在Android系统上,Android退出应用操作只是把应用退到后台,并没有释放内存,当运行时间较长,启动多个应用后,这些应用占满HIGHMEM zone后,就会去占用Normal zone和Highmem zone的内存。在这里我们切断zone_list,就是防止贪得无厌的Android应用和Vmalloc占据Normal和DMA zone。

此外,linux kernel本身对待cache(此cache不是物理cache,而是指buffer cache, inode cache, dentry cache)也是有求必应的,Normal zone分配完,就使用DMA zone,直到把DMA zone占完为止。因此我们实在是有必要禁止cache这种贪得无厌的东西进入DMA zone。

这种截断的做法虽然背离了linux尽量使用系统内存的做法,但是却保证了三个内存区 DMA zone, Normal zone, Highmem zone互不干扰。

lowmem_reserve

上面提到了切断HIGHMEM zone分配失败回退到Normal zone和DMA zone,以及切断Normal zone失败回退到DMA zone。具体做法是配置lowmem_reserve

通过配置lowmem_reserve的为1,使得本内存zone针对高端分配保留尽可能多的空间,来减少fallback分配,这里用减少而不是禁止是因为lowmem_reserve算法在某些内存配置下,无法完全禁止fallback。

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

    关注

    87

    文章

    11292

    浏览量

    209323
  • 碎片化
    +关注

    关注

    0

    文章

    5

    浏览量

    1708

原文标题:避免物理内存碎片化

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内存管理是什么,Linux内存管理详解

    Linux内存管理 Linux内存管理是一个非常复杂的过程,主要分成两个大的部分:内核的内存管理和进程虚拟
    的头像 发表于 05-11 17:54 6039次阅读
    <b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b>管理是什么,<b class='flag-5'>Linux</b>的<b class='flag-5'>内存</b>管理详解

    linux内存相关知识科普

    linux 内存组织结构和页面布局,内存碎片产生原因和优化算法。
    发表于 08-08 10:57 389次阅读

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

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

    Linux内核的物理内存组织结构详解

    Linux内存管理子系统使用 节点(node)、区域(zone)和页(page) 三级结构描述物理内存
    发表于 08-21 15:35 565次阅读
    <b class='flag-5'>Linux</b>内核的<b class='flag-5'>物理</b><b class='flag-5'>内存</b>组织结构详解

    Linux内核内存规整总结

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

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

    的主要优点是避免了外部碎片,而缺点是需要修改内核页表。显然,非连续内存区域的大小必须是4096的倍数。Linux使用非连续物理
    的头像 发表于 02-23 09:44 944次阅读
    <b class='flag-5'>Linux</b>内核<b class='flag-5'>内存</b>管理之内核非连续<b class='flag-5'>物理</b><b class='flag-5'>内存</b>分配

    Linux内存系统: Linux 内存分配算法

    ,便于内存管理,防止内存泄露· 缺点:大量的内存碎片会使系统缓慢,内存使用率低,浪费大2) 如何避免
    发表于 08-24 07:44

    如何避免内存碎片的产生

    考试题型:单选 8 道,不定项 2 道,编程 2 道,问答 1 道考点:进程间通讯的方式,linux的基本指令(考了grep,chmod),根据代码给出运行的结果问答题:简述内存碎片的定义,如何
    发表于 12-21 07:50

    Linux虚拟内存物理内存的深刻分析

    内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。另外,事实上,在每个进程创建加载时,内核只
    发表于 05-31 08:00

    你知道linux kernel内存碎片防治技术?

    Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片
    发表于 05-10 10:59 950次阅读

    OPPO内存碎片引擎的作业

    OPPO宣传的内存碎片引擎是个什么东西?它有何作用?针对上述问题,ColorOS官方微博特地发文科普,我们一起来看看。
    的头像 发表于 03-15 16:20 3580次阅读

    一文解析Linux内存碎片整理原理

    内存整理也是这样,看起来很简单,但实现起来就不那么简单了。因为在内存整理后,需要修正进程的虚拟内存物理内存之间的映射关系。
    发表于 12-28 12:41 762次阅读

    FreeRTOS内存碎片是怎么来的

    内存碎片 在看 FreeRTOS的内存分配方法之前我们先来看一下什么叫做内存碎片,看名字就知道是小块的、
    的头像 发表于 07-30 10:29 887次阅读
    FreeRTOS<b class='flag-5'>内存</b><b class='flag-5'>碎片</b>是怎么来的

    什么是内存碎片Linux

    什么是内存碎片内存碎片Linux很早的时候就已经出现了,了解早期内存
    的头像 发表于 10-08 10:12 717次阅读
    什么是<b class='flag-5'>内存</b><b class='flag-5'>碎片</b><b class='flag-5'>Linux</b>

    如何解决内存碎片内存交换效率慢的问题

    内存分页 分段的好处是能产生连续的内存空间,但是会出现大量内存碎片内存交换效率低的问题 先思考一下怎么解决这两个问题,
    的头像 发表于 10-09 16:57 1021次阅读
    如何解决<b class='flag-5'>内存</b><b class='flag-5'>碎片</b>与<b class='flag-5'>内存</b>交换效率慢的问题