Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片正式buddy system的弱点之一,为了预防以及解决碎片问题,kernel采取了一些实用技术,这里将对这些技术进行总结归纳。

1 低内存时整合碎片

从buddy申请内存页,如果找不到合适的页,则会进行两步调整内存的工作,compact和reclaim。前者是为了整合碎片,以得到更大的连续内存;后者是回收不一定必须占用内存的缓冲内存。这里重点了解comact,整个流程大致如下:

__alloc_pages_nodemask
-> __alloc_pages_slowpath
-> __alloc_pages_direct_compact
-> try_to_compact_pages
-> compact_zone_order
-> compact_zone
-> isolate_migratepages
-> migrate_pages
-> release_freepages

并不是所有申请不到内存的场景都会compact,首先要满足order大于0,并且gfp_mask携带__GFP_FS和__GFP_IO;另外,需要zone的剩余内存情况满足一定条件,kernel称之为“碎片指数”(fragmentation index),这个值在0~1000之间,默认碎片指数大于500时才能进行compact,可以通过proc文件extfrag_threshold来调整这个默认值。fragmentation index通过fragmentation_index函数来计算:

/*
* Index is between 0 and 1000
*
* 0 => allocation would fail due to lack of memory
* 1000 => allocation would fail due to fragmentation
*/
return 1000 - div_u64( (1000+(div_u64(info->free_pages * 1000ULL, requested))), info->free_blocks_total)

在整合内存碎片的过程中,碎片页只会在本zone的内部移动,将位于zone低地址的页尽量移到zone的末端。申请新的页面位置通过compaction_alloc函数实现。

移动过程又分为同步和异步,内存申请失败后第一次compact将会使用异步,后续reclaim之后将会使用同步。同步过程只移动当面未被使用的页,异步过程将遍历并等待所有MOVABLE的页使用完成后进行移动。

2 按可移动性组织页

按照可移动性将内存页分为以下三个类型:

  • UNMOVABLE:在内存中位置固定,不能随意移动。kernel分配的内存基本属于这个类型;
  • RECLAIMABLE:不能移动,但可以删除回收。例如文件映射内存;
  • MOVABLE:可以随意移动,用户空间的内存基本属于这个类型。

申请内存时,根据可移动性,首先在指定类型的空闲页中申请内存,每个zone的空闲内存组织方式如下:

struct zone {
......
struct free_area free_area[MAX_ORDER];
......
}
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};

当在指定类型的free_area申请不到内存时,可以从备用类型挪用,挪用之后的内存就会释放到新指定的类型列表中,kernel把这个过程称为“盗用”。

备用类型优先级列表如下定义:

static int fallbacks[MIGRATE_TYPES][4] = {
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
#ifdef CONFIG_CMA
[MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */
#else
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
#endif
[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */
#ifdef CONFIG_MEMORY_ISOLATION
[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */
#endif
};

值得注意的是并不是所有场景都适合按可移动性组织页,当内存大小不足以分配到各种类型时,就不适合启用可移动性。有个全局变量来表示是否启用,在内存初始化时设置:

void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone)
{
......
if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES))
page_group_by_mobility_disabled = 1;
else
page_group_by_mobility_disabled = 0;
......
}

如果page_group_by_mobility_disabled,则所有内存都是不可移动的。其中有个参数决定了每个内存区域至少拥有的页,pageblock_nr_pages,它的定义如下:

#define pageblock_order HUGETLB_PAGE_ORDER
#else /* CONFIG_HUGETLB_PAGE */
/* If huge pages are not used, group by MAX_ORDER_NR_PAGES */
#define pageblock_order (MAX_ORDER-1)
#endif /* CONFIG_HUGETLB_PAGE */
#define pageblock_nr_pages (1UL << pageblock_order)

在系统初始化期间,所有页都被标记为MOVABLE:

void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
unsigned long start_pfn, enum memmap_context context)
{
......
if ((z->zone_start_pfn <= pfn)
&& (pfn < zone_end_pfn(z))
&& !(pfn & (pageblock_nr_pages - 1)))
set_pageblock_migratetype(page, MIGRATE_MOVABLE);
......
}

其它可移动性类型的页都是后来产生的,也就是前面说的“盗取”。在这种情况发生时,通常会“盗取”fallback中更高优先级、更大块连续的页,从而避免小碎片的产生。

/* Remove an element from the buddy allocator from the fallback list */
static inline struct page *
__rmqueue_fallback(struct zone *zone, int order, int start_migratetype)
{
......
/* Find the largest possible block of pages in the other list */
for (current_order = MAX_ORDER-1; current_order >= order;
--current_order) {
for (i = 0;; i++) {
migratetype = fallbacks[start_migratetype][i];
......
}

可以通过/proc/pageteypeinfo查看当前系统各种类型的页分布。

3 虚拟可移动内存域

在依据可移动性组织页的技术之前,还有一个方法已经合入kernel,那就是虚拟内存域:ZONE_MOVABLE。基本思想很简单:把内存分为两部分,可移动的和不可移动的。

enum zone_type {
#ifdef CONFIG_ZONE_DMA
ZONE_DMA,
#endif
#ifdef CONFIG_ZONE_DMA32
ZONE_DMA32,
#endif
ZONE_NORMAL,
#ifdef CONFIG_HIGHMEM
ZONE_HIGHMEM,
#endif
ZONE_MOVABLE,
__MAX_NR_ZONES
};

ZONE_MOVABLE的启用需要指定kernel参数kernelcore或者movablecore,kernelcore用来指定不可移动的内存数量,movablecore指定可移动的内存大小,如果两个都指定,取不可移动内存数量较大的一个。如果都不指定,则不启动。

与其它内存域不同的是ZONE_MOVABLE不关联任何物理内存范围,该域的内存取自高端内存域或者普通内存域。find_zone_movable_pfns_for_nodes用来计算每个node中ZONE_MOVABLE的内存数量,采用的内存区域通常是每个node的最高内存域,在函数find_usable_zone_for_movable中体现。

在对每个node分配ZONE_MOVABLE内存时,kernelcore会被平均分配到各个Node:

kernelcore_node = required_kernelcore / usable_nodes;

在kernel alloc page时,如果gfp_flag同时指定了__GFP_HIGHMEM和__GFP_MOVABLE,则会从ZONE_MOVABLE内存域申请内存。

linux kernel内存碎片防治技术的更多相关文章

  1. linux内存碎片防治技术

    Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片正式buddy system的弱点之一,为了预防以及解决碎片问题,kernel采取了一些实用技术,这 ...

  2. Linux kernel学习-内存管理【转】

    转自:https://zohead.com/archives/linux-kernel-learning-memory-management/ 本文同步自(如浏览不正常请点击跳转):https://z ...

  3. linux kernel内存映射实例分析

    作者:JHJ(jianghuijun211@gmail.com)日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm cortex ...

  4. Linux kernel pwn notes(内核漏洞利用学习)

    前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...

  5. Linux kernel学习-内存管理

    转自:https://zohead.com/archives/linux-kernel-learning-memory-management/ 本文同步自(如浏览不正常请点击跳转):https://z ...

  6. Linux kernel make 常用选项介绍

    Linux kernel 编译方法大全记录 一.这是一个我自己写的自动make脚本: #!/bin/sh export ARCH=arm export CROSS_COMPILE=arm-linux- ...

  7. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  8. Linux Kernel 代码艺术——编译时断言

    本系列文章主要写我在阅读Linux内核过程中,关注的比较难以理解但又设计巧妙的代码片段(不关注OS的各个模块的设计思想,此部分我准备写在“深入理解Linux Kernel” 系列文章中),一来通过内核 ...

  9. linux kernel的中断子系统 softirq

    linux kernel的中断子系统之(八):softirq http://www.wowotech.net/irq_subsystem/soft-irq.html http://www.ibm.co ...

随机推荐

  1. python高级-装饰器(19)

    一.什么是闭包 先看一个例子: #定义一个函数 def test(number): #在函数内部在定义一个函数,并且这个函数用到外围函数的变量 #那么将这个函数及用到的一些变量称之为闭包 def te ...

  2. CkEditor批量上传图片(java)

    CKEditor上传视频CKEditor批量上传图片flvplayer.swf播放器CKEditor整合包(v4.6.1) ------------------------------------ 最 ...

  3. [Abp 源码分析]十四、DTO 自动验证

    0.简介 在平时开发 API 接口的时候需要对前端传入的参数进行校验之后才能进入业务逻辑进行处理,否则一旦前端传入一些非法/无效数据到 API 当中,轻则导致程序报错,重则导致整个业务流程出现问题. ...

  4. 我曾做过陈士成,也做过孔乙己,还做过阿Q

    一. 我现在是陈士成,陈士成现在是我.为什么这么说呢? 那年那天,天刚微微亮,似乎还在打着哈欠.我和父亲去得很早,为的就是在“小升初的考试成绩榜单”前面占一个有利的位置.我不记得当时穿的厚还是不厚,体 ...

  5. HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你!

    前言 Map 这样的 Key Value 在软件开发中是非常经典的结构,常用于在内存中存放数据. 本篇主要想讨论 ConcurrentHashMap 这样一个并发容器,在正式开始之前我觉得有必要谈谈 ...

  6. oracle调整内存大小

    1.查看已分配内存,看到memory_max_target为20GSQL> show parameter sga NAME                     TYPE     VALUE- ...

  7. GitLab 社区版 11.0.2用户管理教程

    Gitlab地址:http://xx.xx.xx.xx/users/sign_in 登录之后,主界面 右上角功能按钮,分别为管理区域.新建.问题.合并请求.待办事件.个人设置 选择管理区域按钮 可以根 ...

  8. asp.net core 系列 5 MVC框架路由(上)

    一. 概述 介绍asp.net core路由时,我初步想了下,分几篇来说明.  路由的知识点很多,参考了官方文档提取出一些重要的知识点来说.    在ASP.NET Core中是使用路由中间件来匹配传 ...

  9. 【干货】Chrome插件(扩展)开发全攻略

    写在前面 我花了将近一个多月的时间断断续续写下这篇博文,并精心写下完整demo,写博客的辛苦大家懂的,所以转载务必保留出处.本文所有涉及到的大部分代码均在这个demo里面:https://github ...

  10. 【朝花夕拾】Android编码风格篇

    结合51CTO学院中张凌华老师讲的编码风格课程,对自己平时工作中的形成的一些编码风格做一些总结. 一. 项目开发目录命名: Requirement - 需求相关文档 Design - 设计 Plann ...