linux内存碎片防治技术

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函数来计算:

  1. /*
  2. * Index is between 0 and 1000
  3. *
  4. * 0 => allocation would fail due to lack of memory
  5. * 1000 => allocation would fail due to fragmentation
  6. */
  7. 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的空闲内存组织方式如下:

  1. struct zone {
  2. ......
  3. struct free_area free_area[MAX_ORDER];
  4. ......
  5. }
  6. struct free_area {
  7. struct list_head free_list[MIGRATE_TYPES];
  8. unsigned long nr_free;
  9. };

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

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

  1. static int fallbacks[MIGRATE_TYPES][4] = {
  2. [MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
  3. [MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
  4. #ifdef CONFIG_CMA
  5. [MIGRATE_MOVABLE] = { MIGRATE_CMA, MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
  6. [MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */
  7. #else
  8. [MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
  9. #endif
  10. [MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */
  11. #ifdef CONFIG_MEMORY_ISOLATION
  12. [MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */
  13. #endif
  14. };

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

  1. void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone)
  2. {
  3. ......
  4. if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES))
  5. page_group_by_mobility_disabled = 1;
  6. else
  7. page_group_by_mobility_disabled = 0;
  8. ......
  9. }

如果page_group_by_mobility_disabled,则所有内存都是不可移动的。

其中有个参数决定了每个内存区域至少拥有的页,pageblock_nr_pages,它的定义如下:

#define pageblock_order HUGETLB_PAGE_ORDER

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

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

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

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

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

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

3 虚拟可移动内存域

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

  1. enum zone_type {
  2. #ifdef CONFIG_ZONE_DMA
  3. ZONE_DMA,
  4. #endif
  5. #ifdef CONFIG_ZONE_DMA32
  6. ZONE_DMA32,
  7. #endif
  8. ZONE_NORMAL,
  9. #ifdef CONFIG_HIGHMEM
  10. ZONE_HIGHMEM,
  11. #endif
  12. ZONE_MOVABLE,
  13. __MAX_NR_ZONES
  14. };

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内存域申请内存。

原文地址:https://www.cnblogs.com/alantu2018/p/8447524.html

时间: 2024-10-12 01:40:22

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

linux内存碎片的概念

一般在内核术语中的“碎片”都是基于物理内存而言的,我没有太看懂你得出碎片是针对地址空间这个结论的逻辑.但我认为,既然你知道了malloc是用户空间调用的,那么你所谓的碎片也是从用户空间的视角而言的,但对于用户空间,看到的只有地址空间,其访问的所有地址都是要经过页表的转换后才访问的物理页面,而且malloc分配的地址空间一定的连续的,那么这个所谓的碎片对于用户空间也就没有任何意义了.不知道你纠结的这个“碎片”在你心目中到底是什么意义,另外,你说的“如果地址空间都不连续就更谈不上物理空间连续”,这个

Linux运维(十)-2016-12-9整理

也有段时间没有整理面试题目了,这几天呢,完成了2场实习生和1场校招,十分郁闷,为什么金融运维一点都不关注技术,在乎我的学校成绩,我尼玛想说,学校那么水的课程能说明什么,跟技术有一丁丁点关系吗?我学业成绩不好,就能否定我的专业岗位基础不行吗?唉,劳资想说,你如果质疑我的能力,你就问我,劳资要是被你虐倒是我算我输,学艺不精我就服,看过去有球用. 这辈子职业生涯都不想进外包和IDC这两种类型的公司,就是TM打杂的,薪资还低,关键是不厚道,比如垃圾胜蓝.这回校招那家是我之前就提到的那家,大多数人对我是认

linux 系统 /proc

1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口. 用户和应用程序可以通过 proc得到系统的信息,并可以改变内核的某些参数.由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是 动态从系统内核读出所需信息并提交的.下面列出的这些文件或子文件夹,并不是都是在你的

linux内核探索之内存管理(二):linux系统中的内存组织--结点、内存域和页帧

本文主要参考<深入linux内核架构>(3.2节)及Linux3.18.3内核源码 概述:本文主要描述了内存管理相关的数据结构:结点pg_data_t.内存域struct zone以及页帧(物理页):struct page ,以及该结构相关的一些基本概念. 1. 概述 内存划分为接点,每个结点关联到系统中的一个处理器,在内核中表示为pg_data_t. 各个结点又划分为内存域,比如DMA内存域,高端内存域,普通内存域. 内核内存域的宏: enum zone_type { #ifdef CONF

Linux内存管理机制

一.首先大概了解一下计算机CPU.Cache.内存.硬盘之间的关系及区别. 1.  CPU也称为中央处理器(CPU,Central Processing Unit)是一块超大规模的集成电路, 是一台计算机的运算核心(Core)和控制核心( Control Unit).它的功能主要是解释计算机指令以及处理计算机软件中的数据.中央处理器主要由三核心部件组成,运算器.控制器和总线(BUS),运算器又主要由算术逻辑单元(ALU)和寄存器(RS)组成. 2.Cache即高速缓冲存储器,是位于CPU与主内存

Linux内存管理 【转】

转自:http://blog.chinaunix.net/uid-25909619-id-4491368.html Linux内存管理 摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用.在本章最后,我们给出一个内存映射的实例,帮助网友们理解内核内存管理与用户内存管理之间的关系,希望大家最终能驾驭Linux内存管理. 前言 内存管理一向是所有操作系统书

【转】Linux下/proc目录简介

1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口. 用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数.由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的.下面列出的这些文件或子文件夹,并不是都是在你的系统

Linux下/proc目录简介

1. /proc目录Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构.改变内核设置的机制.proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口. 用户和应用程序可以通过proc得到系统的信息,并可以改变内核的某些参数.由于系统的信息,如进程,是动态改变的,所以用户或应用程序读取proc文件时,proc文件系统是动态从系统内核读出所需信息并提交的.下面列出的这些文件或子文件夹,并不是都是在你的系统

【转】 linux内存管理

一 为什么需要使用虚拟内存 大家都知道,进程需要使用的代码和数据都放在内存中,比放在外存中要快很多.问题是内存空间太小了,不能满足进程的需求,而且现在都是多进程,情况更加糟糕.所以提出了虚拟内存,使得每个进程用于3G的独立用户内存空间和共享的1G内核内存空间.(每个进程都有自己的页表,才使得3G用户空间的独立)这样进程运行的速度必然很快了.而且虚拟内存机制还解决了内存碎片和内存不连续的问题.为什么可以在有限的物理内存上达到这样的效果呢? 二 虚拟内存的实现机制 首先呢,提一个概念,交换空间(sw