当前位置:首页 > 公众号精选 > Linux阅码场
[导读]已经运行了一段时间的系统上会产生大量不连续的page, 要想找到高阶(high-order)条件的内存空间具有挑战性。

作者简介

赵金生,linux内核爱好者,就职于杭州某大型安防公司,担任Linux BSP软件工程师。对进程调度,内存管理有所了解。希望能通过对linux的学习,提升产品软件性能及稳定性。该文章为私人学习总结,不存在公司网络安全问题。

memory compaction简介

随着系统的运行,经过不同用户的分配请求后,页框会变得十分分散,导致此段页框被这些正在使用的零散页框分为一小段一小段非连续页框,这使得在需要分配内存时很难找到物理上连续的页框。

现代处理器不再限于使用传统的4K大小的页框;它们可以在进程的部分地址空间中支持大得多的页(huge pages)。使用巨页会带来真正的性能优势,主要原因是减小了对处理器的转换后备缓冲区(translation lookaside buffer)的压力。但是使用巨页要求系统能够找到物理上连续的内存区域,这些区域不仅要足够大,而且还必须确保按适当方式满足字节对齐的要求。

在一个已经运行了一段时间的系统上会产生大量的不连续的page, 要想找到符合这些高阶(high-order)条件的内存空间非常具有挑战性,memory compaction的作用就是解决high-order内存分配失败问题,与buddy system机制做一个互补。

memory compaction原理

内存碎片整理以pageblock为单位。
在内存碎片整理开始前,会在zone的头和尾各设置一个指针,头指针从头向尾扫描可移动的页,而尾指针从尾向头扫描空闲的页,当他们相遇时终止整理。

简单示意图:需要明确的是:实际情况并不是与图示的情况完全一致。头指针每次扫描一个符合要求的pageblock里的所有页框,当pageblock不为MIGRATE_MOVABLE、MIGRATE_CMA、MIGRATE_RECLAIMABLE时会跳过这些pageblock,当扫描完这个pageblock后有可移动的页框时,会变为尾指针以pageblock为单位向前扫描可移动页框数量的空闲页框,但是在pageblock中也是从开始页框向结束页框进行扫描,最后会将前面的页框内容复制到这些空闲页框中。

这里的移动是将页框中的数据copy拷贝到可移动的空闲页框当中,此时原有的movable page变成free page。所以并不是页框自身的移动而是数据的移动。

通过下图的操作就可以分配出一个order = 2或者是order =  3的连续的可用空间,可用于满足更high-order的内存分配。当然,这里展示的流程和真实系统比起来已经大大简化了。实际的内存域会大得多,这意味着扫描的工作量也会大很多,但由此获得的空闲区也可能更大。


实际的内存碎片,还有一个问题就是在整理算法中会将扫描中识别为不满足整理要求的内存块标识为 “可忽略”(“skip”,即不执行规整)。作为一种优化,目的是防止运行没必要的规整操作。

比如系统正在对zone进行内存碎片整理,首先,会从可移动页框开始位置向后扫描一个pageblock,得到一些可移动页框,然后空闲页框从开始位置向前扫描一个pageblock,得到一些空闲页框,然后将可移动页框移动到空闲页框中,之后再继续循环扫描。对一个pageblock进行扫描后,如果无法从此pageblock隔离出一个要求的页框,这时候就会将此pageblock标记为跳过(skip)

假设内存碎片整理可移动页扫描是从zone的第一个页框开始,扫描完一个pageblock后,没有隔离出可移动页框,则标记此pageblock的跳过标记PB_migrate_skip,然后将zone->compact_cached_migrate_pfn设置为此pageblock的结束页框。
这样,在下次对此zone进行内存碎片整理时,就会直接从此pageblock的下一个pageblock开始,把此pageblock跳过了。同理,对于空闲页扫描也是一样。这样就必须更新zone pageblock的起始地址与结束地址:


以上就是内存碎片整理的基本原理了。

memory compaction如何实现

3.1、数据结构

在内存碎片整理中,可以移动的页框有MIGRATE_RECLAIMABLE、MIGRATE_MOVABLE与MIGRATE_CMA这三种类型的页框。

而因为内存碎片整理分为同步和异步。在异步过程中,只会移动MIGRATE_MOVABLE和MIGRATE_CMA这两种类型的页框。因为这两种类型的页框处理,是不会涉及到IO操作的。而在同步过程中,这三种类型的页框都会进行移动,因为MIGRATE_RECLAIMABLE基本上都是文件页,在移动过程中,有可能要将脏页回写,会涉及到IO操作,也就是在同步过程中,是会涉及到IO操作的。

1、migrate_mode迁移模式:

enum migrate_mode { MIGRATE_ASYNC, MIGRATE_SYNC_LIGHT, MIGRATE_SYNC,};
2、compact_priority

enum compact_priority { COMPACT_PRIO_SYNC_FULL, MIN_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_FULL, COMPACT_PRIO_SYNC_LIGHT, MIN_COMPACT_COSTLY_PRIORITY = COMPACT_PRIO_SYNC_LIGHT, DEF_COMPACT_PRIORITY = COMPACT_PRIO_SYNC_LIGHT, COMPACT_PRIO_ASYNC, INIT_COMPACT_PRIORITY = COMPACT_PRIO_ASYNC};
3、compact_result用于压缩处理函数的返回值

enum compact_result { /* For more detailed tracepoint output - internal to compaction */ COMPACT_NOT_SUITABLE_ZONE,//trace用于调试输出或内部使用 /* * compaction didn't start as it was not possible or direct reclaim * was more suitable */ COMPACT_SKIPPED,//跳过压缩,因为无法执行压缩或直接回收更合适 /* compaction didn't start as it was deferred due to past failures */ COMPACT_DEFERRED,
/* compaction not active last round */ COMPACT_INACTIVE = COMPACT_DEFERRED,
/* For more detailed tracepoint output - internal to compaction */ COMPACT_NO_SUITABLE_PAGE, /* compaction should continue to another pageblock */ COMPACT_CONTINUE,
/* * The full zone was compacted scanned but wasn't successfull to compact * suitable pages. */ COMPACT_COMPLETE,//已完成所有区域的压缩,但是尚未确保可以通过压缩分配的页面 /* * direct compaction has scanned part of the zone but wasn't successfull * to compact suitable pages. */ COMPACT_PARTIAL_SKIPPED,
/* compaction terminated prematurely due to lock contentions */ COMPACT_CONTENDED,
/* * direct compaction terminated after concluding that the allocation * should now succeed */ COMPACT_SUCCESS,//在确保可分配页面安全后,直接压缩结束};

4、compact_control需要进行内存碎片整理时,总是需要初始化该结构体

struct compact_control { /* 扫描到的空闲页的页的链表 */ struct list_head freepages; /* List of free pages to migrate to */ /* 扫描到的可移动的页的链表 */ struct list_head migratepages; /* List of pages being migrated */ /* 空闲页链表中的页数量 */ unsigned long nr_freepages; /* Number of isolated free pages */ /* 可移动页链表中的页数量 */ unsigned long nr_migratepages; /* Number of pages to migrate */ /* 空闲页框扫描所在页框号 */ unsigned long free_pfn; /* isolate_freepages search base */ /* 可移动页框扫描所在页框号 */ unsigned long migrate_pfn; /* isolate_migratepages search base */ /* 内存碎片整理使用的模式: 同步,轻同步,异步 */ enum migrate_mode mode; /* Async or sync migration mode */ /* 是否忽略pageblock的PB_migrate_skip标志对需要跳过的pageblock进行扫描 ,并且也不会对pageblock设置跳过 * 只有两种情况会使用 * 1.调用alloc_contig_range()尝试分配一段指定了开始页框号和结束页框号的连续页框时; * 2.通过写入1到sysfs中的/vm/compact_memory文件手动实现同步内存碎片整理。 */ bool ignore_skip_hint; /* Scan blocks even if marked skip */ /* 本次内存碎片整理是否隔离到了空闲页框,会影响zone的空闲页扫描起始位置 */ bool finished_update_free; /* True when the zone cached pfns are * no longer being updated */ /* 本次内存碎片整理是否隔离到了可移动页框,会影响zone的可移动页扫描起始位置 */ bool finished_update_migrate; /* 申请内存时需要的页框的order值 */ int order; /* order a direct compactor needs */ const gfp_t gfp_mask; /* gfp mask of a direct compactor */ /* 扫描的管理区 */ struct zone *zone; /* 保存结果,比如异步模式下是否因为需要阻塞而结束了本次内存碎片整理 */ int contended; /* Signal need_sched() or lock * contention detected during * compaction */};

5、Node zone 扫描推迟

struct zone{ ..... unsigned int compact_considered; unsigned int compact_defer_shift; int compact_order_failed; ......} 当一个zone要进行内存碎片整理时,首先会判断本次整理需不需要推迟,如果本次内存碎片整理使用的order值小于zone内存碎片整理失败最大order值compact_order_failed时,不用进行推迟,可以直接进行内存碎片整理;
当order值大于zone内存碎片整理失败最大order值compact_order_failed,会增加内存碎片整理推迟计数器compact_considered,如果内存碎片整理推迟计数器compact_considered未达到内存碎片整理推迟阀值defer_limit,则会跳过本次内存碎片整理,如果达到了,那就需要进行内存碎片整理。

总结:也就是当order小于zone内存碎片整理失败最大order值时,不用进行推迟,而order大于zone内存碎片整理失败最大order值时,才考虑是否进行推迟,此时推迟就是continue扫描node当中的下一个zone区域,这里并不是想下文一下设置zone SKIP标志。
6、Pageblock skip

struct zone{ ...... unsigned long compact_cached_free_pfn; /* pfn where async and sync compaction migration scanner should start */ unsigned long compact_cached_migrate_pfn[2];
......}

3.2、源码分析

内存碎片整理移动发生条件:
  • 内存分配不足时触发direct compact整理内存
  • Kswapd内存回收后唤醒kcompactd内核线程执行compact操作,获取连续内存
  • 手动设置echo 1 > /proc/sys/vm/compact_memory


分析的重点就放在内存分配不足的情况,入口函数从try_to_compact_pages开始

对源码详细分析参见代码:https://github.com/linuxzjs/linux-4.14

重点分析5个关键函数:

1、compaction_suitable

/* 判断该zone是否可以做内存碎片压缩整理 */enum compact_result compaction_suitable(struct zone *zone, int order, unsigned int alloc_flags, int classzone_idx){ enum compact_result ret; int fragindex; /* * 根据watermask判断zone中离散的page是否满足2^order的内存分配请求,如果满足则继续对zone进行内存的compact整理zone的内存碎片 * 说明该zone时可以做内存碎片的压缩整理的。 */ ret = __compaction_suitable(zone, order, alloc_flags, classzone_idx,zone_page_state(zone, NR_FREE_PAGES));
/* 如果return返回值为COMPACT_CONTINUE,且order > PAGE_ALLOC_COSTLY_ORDER(3)则进入一下判断当中 */ if (ret == COMPACT_CONTINUE

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

为了满足日益增长的数据处理需求,铁威马NAS推出了全新的性能巅峰2024年旗舰之作F4-424 Pro,并搭载了最新的操作系统--TOS 6。这款高效办公神器的问世,无疑将为企业和专业人士带来前所未有的便捷与效率。

关键字: 存储 Linux 服务器

双系统将是下述内容的主要介绍对象,通过这篇文章,小编希望大家可以对双系统的相关情况以及信息有所认识和了解,详细内容如下。

关键字: 双系统 Windows Linux

安装Linux操作系统并不复杂,下面是一个大致的步骤指南,以帮助您完成安装。1. 下载Linux发行版:首先,您需要从Linux发行版官方网站下载最新的ISO镜像文件。

关键字: Linux 操作系统 ISO镜像

计算机是由一堆硬件组成的,为了有限的控制这些硬件资源,于是就有了操作系统的产生,操作系统是软件子系统的一部分,是硬件基础上的第一层软件。

关键字: Linux 操作系统 计算机

2023年10月18日,中国在第三届“一带一路”国际合作高峰论坛期间发布《全球人工智能治理倡议》,围绕人工智能发展、安全、治理三方面系统阐述了人工智能治理中国方案。

关键字: 人工智能 大模型 代码

Linux操作系统是一套免费使用和自由传播的类Unix操作系统,通常被称为GNU/Linux。它是由林纳斯·托瓦兹在1991年首次发布的,并基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。Lin...

关键字: Linux 操作系统

所谓进程间通信就是在不同进程之间传播或交换信息,它是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息;还可以让一个程序能够在同一时间里处理许多用户的需求。

关键字: Linux 进程通信 编程接口

串口通信作为一种最传统的通信方式,在工业自动化、通讯、控制等领域得到广泛使用。

关键字: Linux 串口通信 通讯

我们看到这么多的安全问题,部分原因在于我们对待安全的方式:安全性通常被认为是事后考虑的问题,是在开发结束时才添加到设备上的东西。然而,复杂的系统,尤其是嵌入式系统,有一个很大的攻击面,这让攻击者有机可乘,能够在“盔甲”上...

关键字: 代码 嵌入式系统 软件漏洞

2023年11月16日: MikroElektronika(MIKROE) ,作为一家通过提供基于成熟标准的创新式硬软件产品来大幅缩短开发时间的嵌入式解决方案公司,今天宣布推出一款基于单线设备的软硬件开源解决方案Cli...

关键字: 嵌入式 Linux 操作系统
关闭
关闭