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函数来计算:
- /*
- * 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内存碎片防治技术的更多相关文章
- linux kernel内存碎片防治技术
Linux kernel组织管理物理内存的方式是buddy system(伙伴系统),而物理内存碎片正式buddy system的弱点之一,为了预防以及解决碎片问题,kernel采取了一些实用技术,这 ...
- 关于linux 内存碎片指数
linux针对每一个node的每个zone的每个order,都有一个碎片指数来描述当前的碎片程度,也就是 extfrag_index 参数: extfrag_index这个要展示出来,需要内核编译了两 ...
- linux内存碎片的概念
一般在内核术语中的“碎片”都是基于物理内存而言的,我没有太看懂你得出碎片是针对地址空间这个结论的逻辑.但我认为,既然你知道了malloc是用户空间调用的,那么你所谓的碎片也是从用户空间的视角而言的,但 ...
- Linux 驱动开发
linux驱动开发总结(一) 基础性总结 1, linux驱动一般分为3大类: * 字符设备 * 块设备 * 网络设备 2, 开发环境构建: * 交叉工具链构建 * NFS和tftp服务器安装 3, ...
- jemalloc在linux上从安装到使用
jemalloc在linux上从安装到使用 上次在引导大家安装Redis时提到可能会报错: 发现了redis有用到jemalloc. 首先,jemalloc是干什么的? 我们看看作者自己的介绍: j ...
- 【转】 linux内存管理
一 为什么需要使用虚拟内存 大家都知道,进程需要使用的代码和数据都放在内存中,比放在外存中要快很多.问题是内存空间太小了,不能满足进程的需求,而且现在都是多进程,情况更加糟糕.所以提出了虚拟内存,使得 ...
- 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册
<Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...
- MySQL 调优基础(二) Linux内存管理
进程的运行,必须使用内存.下图是Linux中进程中的内存的分布图: 其中最重要的 heap segment 和 stack segment.其它内存段基本是大小固定的.注意stack是向低地址增长的, ...
- Linux内存管理原理
本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...
随机推荐
- Multi-label && Multi-label classification
Multi-label classification with Keras In today’s blog post you learned how to perform multi-label cl ...
- python3 UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 30: illegal multibyte sequence
昨天用用python3写个日志文件,结果报错UnicodeEncodeError: 'gbk' codec can't encode character '\xa0' in position 30: ...
- python3 操作sqlSever
相关代码如下: #coding =utf-8 import os import pyodbc import time class SqlDb: def __init__(self, server='D ...
- golang struct转map
struct转map package main import ( "fmt" "reflect" "time" ) type User st ...
- GO语言基础之并发concurrency
并发Concurrency 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已.不过话说回来,每个实例 4-5KB的 ...
- uploadify的java应用
API:http://www.uploadify.com/documentation/ 下载地址:http://www.uploadify.com/ 这几天查看插件,发现uploadify插件做不错, ...
- Bootstrap3免费单页面模板-Shuffle
在线演示 本地下载 这是一款当前最热门的模板,单页面模板现在越来越时兴,它简洁的页面和每一次滑动都带来的全新视角.非常值得收藏和使用!
- HDU 1199 && ZOJ 2301 线段树离散化
一段长度未知的线段.一种操作:a b c ,表示区间[a,b]涂为颜色C,w代表白色,b代表黑色,问终于的最长连续白色段,输出起始位置和终止位置 离散化处理.和寻常的离散化不同,须要把点化成线段.左闭 ...
- qtp:vbs基础教程
◎Vbs脚本编程简明教程之中的一个-为什么要使用Vbs? 在Windows中,学习计算机操作或许非常easy,可是非常多计算机工作是反复性劳动,比如你每周或许须要对一些计算机文件进行复制.粘贴.改名 ...
- The platform of the target `Pods` (iOS 4.3) is not compatible 错误
一:使用 cocoaPod错误 The platform of the target `Pods` (iOS 4.3) is not compatible with `AFNetworking (1. ...