如何从GFP确定最后申请的内存来自哪个zone?
本文基于linux-3.10的内核
申请内存时需要先确定两个值,分别是high_zoneidx和migratetype。这两个值从哪里获取到呢?都是利用上面传递过来的GFP flag。
对于high_zoneidx是通过gfp_zone函数获取的,而migratetype是通过gfpflags_to_migratetype来获取的。其中gfp_zone是利用的GFP标志位的bit[0-3]这4个bits来确定的,而migratetype是bit[3-4]这两个bits。需要注意他们中间是存在重复的__GFP_MOVABLE标志的,说明它不仅仅用来确定zone也是用来确定migratetype的一个因素。
gfp_zone
#define ___GFP_DMA 0x01u #define ___GFP_HIGHMEM 0x02u#define ___GFP_DMA32 0x04u#define ___GFP_MOVABLE 0x08u
上面是用于寻找zone的4个标志位,每个标志位占用单独一个bit。根据这4个bit相互搭配一个存在16中组合,如下所示:
216 * bit result
217 * =================
218 * 0x0 => NORMAL
219 * 0x1 => DMA or NORMAL
220 * 0x2 => HIGHMEM or NORMAL
221 * 0x3 => BAD (DMA+HIGHMEM)
222 * 0x4 => DMA32 or DMA or NORMAL
223 * 0x5 => BAD (DMA+DMA32)
224 * 0x6 => BAD (HIGHMEM+DMA32)
225 * 0x7 => BAD (HIGHMEM+DMA32+DMA)
226 * 0x8 => NORMAL (MOVABLE+0)
227 * 0x9 => DMA or NORMAL (MOVABLE+DMA)
228 * 0xa => MOVABLE (Movable is valid only if HIGHMEM is set too)
229 * 0xb => BAD (MOVABLE+HIGHMEM+DMA)
230 * 0xc => DMA32 (MOVABLE+HIGHMEM+DMA32)
231 * 0xd => BAD (MOVABLE+DMA32+DMA)
232 * 0xe => BAD (MOVABLE+DMA32+HIGHMEM)
233 * 0xf => BAD (MOVABLE+DMA32+HIGHMEM+DMA)
左边是这些组合得到的值,右边是该组合要申请内存的zone,可以认为上面这个表就是GFP flag到zone的映射表。
内核中规定前三个GFP标志位是互斥的,不能同时设置,而___GFP_MOVABLE则可以搭配设置。因而有一些组合得到的bit值是不符合要求,上表中右边写上BAD的即是这种情况。
GFP和ZONE已然存在上面的这个关系,那么内核的函数实现就是如何快速的查找该表,从GFP组合中快速的寻找ZONE,为了提升性能,内核实现都是利用左移和右移操作来实现,因而构建了一个便于左移和右移查找的一个TABLE:
249 #define GFP_ZONE_TABLE ( \
250 (ZONE_NORMAL << 0 * GFP_ZONES_SHIFT) \
251 | (OPT_ZONE_DMA << ___GFP_DMA * GFP_ZONES_SHIFT) \
252 | (OPT_ZONE_HIGHMEM << ___GFP_HIGHMEM * GFP_ZONES_SHIFT) \
253 | (OPT_ZONE_DMA32 << ___GFP_DMA32 * GFP_ZONES_SHIFT) \
254 | (ZONE_NORMAL << ___GFP_MOVABLE * GFP_ZONES_SHIFT) \
255 | (OPT_ZONE_DMA << (___GFP_MOVABLE | ___GFP_DMA) * GFP_ZONES_SHIFT) \
256 | (ZONE_MOVABLE << (___GFP_MOVABLE | ___GFP_HIGHMEM) * GFP_ZONES_SHIFT)\
257 | (OPT_ZONE_DMA32 << (___GFP_MOVABLE | ___GFP_DMA32) * GFP_ZONES_SHIFT)\
258 )
左边是zone的index,右边是匹配的GFP组合值,加入zone index要么为0,要么为1,那么把zone的index值按照GFP组合值大小来进行左移操作即可把它保存到对应的bit上,该bit上的0和1分别表示不同的两个zone。然而现实是zone index它可能是大于1的值,因而需要GFP_ZONES_SHIFT个bits来保存zone的index,所以右边统一乘以GFP_ZONES_SHIFT。
通过这种方式就创建了一个信息查找表,而查找函数就是如下的函数:
277 static inline enum zone_type gfp_zone(gfp_t flags)
278 {279 enum zone_type z;
280 int bit = (__force int) (flags & GFP_ZONEMASK);
281
282 z = (GFP_ZONE_TABLE >> (bit * GFP_ZONES_SHIFT)) &
283 ((1 << GFP_ZONES_SHIFT) - 1);
284 VM_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
285 return z;
286 }
这个函数就是通过GFP flag利用左移和右移操作快速的查找到对应的ZONE index。
有了这个ZONE index就一定会从这个zone中分配内存了吗?还是想的太天真了,对于内存的申请,内核中存在一个fallback列表,当我们从第一选择zone中去查找空闲内存,如果发现该zone中的空闲内存不足,那么此时就返回失败吗?
当然不是,内核会尝试从fallback zone中去查找空闲内存。比如内核根据GFP flag得到一个zone index,对应的ZONE是Normal Zone,而此时Normal Zone中的空闲内存不足申请失败,但是DMA zone中还存在很多空闲内存,那么此时内核就会从DMA zone中去再次去尝试申请。
这个内存申请的fallback顺序为:
MOVABLE=>HIGHMEM=>NORMAL=>DMA32=>DMA
到这里还涉及到一个问题,那么对于每个Zone来说,如果都可以被其他Zone来申请,大家都可以互相申请,那么区分这些zone还有什么意义呢?
实际上并不是如此,为了保证每个zone中的内存不会被其他zone全部申请走,内核针对每个zone定义了一个保留内存:
zone1 : lowmem_reserve[]: 0 0 126976 126976
它是一个数组,比如上面的数组表示系统存在4个zone,假设为zone1,zone2,zone3,zone4。
当zone3或者zone4尝试从zone1中申请时需要考虑的预留内存是126976个页面。也就是说zone3判断zone1中是否存在足够的空闲内存用于申请时,需要把这个预留内存给去除掉。具体的计算方法如下:
2360 static bool __zone_watermark_ok(struct zone *z, int order, unsigned long mark,
2361 int classzone_idx, int alloc_flags, long free_pages)
2362 {
2363 /* free_pages my go negative - that's OK */
2364 long min = mark;
2365 long lowmem_reserve = z->lowmem_reserve[classzone_idx];
2366 int o;
2367 long free_cma = 0;
2368
2369 free_pages -= (1 << order) - 1;
2370 if (alloc_flags & ALLOC_HIGH)
2371 min -= min / 2;
2372 if (alloc_flags & ALLOC_HARDER)
2373 min -= min / 4;
2374 #ifdef CONFIG_CMA
2375 /* If allocation can't use CMA areas don't use free CMA pages */
2376 if (!(alloc_flags & ALLOC_CMA))
2377 free_cma = zone_page_state(z, NR_FREE_CMA_PAGES);
2378 #endif
2379
2380 if (free_pages - free_cma <= min + lowmem_reserve)
2381 return false;
2382 for (o = 0; o < order; o++) {
2383 /* At the next order, this order's pages become unavailable */
2384 free_pages -= z->free_area[o].nr_free << o;
2385
2386 /* Require fewer higher order pages to be free */
2387 min >>= 1;
2388
2389 if (free_pages <= min)
2390 return false;
2391 }
2392 return true;
2393 }
通过这个函数来判断某一个zone中针对该GFP申请的内存,是否存在空闲页面来完成此次申请,可以看到其中就使用了lowmem_reserve来综合考虑作为判断结果。
numa
static struct page *
2580 get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order,
2581 struct zonelist *zonelist, int high_zoneidx, int alloc_flags,
2582 struct zone *preferred_zone, int migratetype)
2583 {2584 struct zoneref *z;
2585 struct page *page = NULL;
2586 int classzone_idx;
2587 struct zone *zone;
2588 nodemask_t *allowednodes = NULL;/* zonelist_cache approximation */
2589 int zlc_active = 0; /* set if using zonelist_cache */
2590 int did_zlc_setup = 0; /* just call zlc_setup() one time */
2591
2592 classzone_idx = zone_idx(preferred_zone);
2593 zonelist_scan:
2594 /*
2595 * Scan zonelist, looking for a zone with enough free.
2596 * See also cpuset_zone_allowed() comment in kernel/cpuset.c.
2597 */
2598 for_each_zone_zonelist_nodemask(zone, z, zonelist,
2599 high_zoneidx, nodemask) {
最后会尝试在对应nodemask上的zonelist上查找空闲页面,当nodemask为NULL时会扫描所有的node。
如何从GFP确定最后申请的内存来自哪个zone?相关推荐
- 通过伙伴系统申请内核内存的函数有哪些?
本文转自:http://blog.chinaunix.net/space.php?uid=22566367&do=blog&id=2747207 在物理页面管理上实现了基于区的伙伴系统 ...
- kmalloc最大能申请多少内存?
1. 概述 本文主要分析kmalloc接口申请内存的大小情况,用于记录kmalloc分配内存的过程. 内核版本:Linux 4.9 2.分析记录 针对kmalloc最大能申请多少内存,网上众说纷纭,意 ...
- linux 用户进程结束后 malloc申请的内存会自动释放吗,进程退出后malloc的内存是否会被释放?
当一个进程退出后,不管是正常退出或者是异常退出,操作系统都会释放这个进程的资源.包括这个进程分配的内存,打开的文件等等. 内存泄露的前提是进程一直在运行:进程一旦退出,所占的整个虚拟内存都被销毁,所有 ...
- 【三分钟学习FFMPEG一个知识点】FFMPEG关于avio_alloc_context申请使用内存释放问题
问题: 使用ffmpeg发现av_malloc申请的内存最后不能用av_free函数释放,会崩溃. 代码示例: unsigned char * iobuffer = NULL; iobuffer = ...
- Yarn申请的内存的精确计算(转载+应用到自己的情况中)
这篇博客主要是利用[1]中的流程来尝试估算自己在运行spark on yarn模式时申请的内存数值. 一个spark任务会产生几个Container? count = ExecutorNum + 1 ...
- 关于用函数指针参数申请动态内存的问题
今天在写一个Binary Search Tree的程序时,发现其插入有问题,下面是插入程序,每次插入完成后,节点还是NULL. template<typename Object> void ...
- c语言链表错误,C语言创建链表错误之通过指针参数申请动态内存实例分析
本文实例讲述了C语言创建链表中经典错误的通过指针参数申请动态内存,分享给大家供大家参考之用.具体实例如下: #include #include // 用malloc要包含这个头文件 typedef s ...
- C语言为四维数组申请动态内存空间的方法(二)
尝试了用堆栈的方式为四维数组申请动态内存空间,并将申请内存的操作封装成了子函数,方便在主程序中使用.希望对大家有用.代码如下: #include <stdio.h> #include &l ...
- C语言为四维数组申请动态内存空间的方法(一)
尝试了用结构体为四维数组申请动态内存空间,希望对大家有用.代码如下: #include <stdio.h> #include <stdlib.h>typedef struct ...
最新文章
- 关于自注意力机制的思考
- 高级指令——hostname指令【作用:操作服务器的主机名(读取、设置)】、id指令【查看一个用户的一些基本信息(包含用户id,用户组id,附加组id…)】、whoami指令、ps -ef指令
- 虚拟现实大会ChinaVR2015报告之-From Visual Content to Virtual Reality Data-driven Intelligence Production
- 谁在为网易云音乐2亿用户的即时通讯保驾护航?
- 在c语言中large的作用,输入两个数组,调用large函数比较,计数,输出统计结果...
- 三大器之------生成器
- Tensorflow2.0.0版本和Keras2.4.3不兼容
- 谨以此送给所有单身的MM
- 春节回来,陪你去看星河云!
- 变频器压频比的正确设置方法
- SetupFactory使用
- 抖音热门音乐整理合集歌曲打包分享
- 行人重识别实验笔记3-JDAI fast-reid项目配置
- win7共享wifi自动获取不到ip地址的解决办法
- 每周分享第 23 期
- 自动化测试常见面试问题
- 在C和C++定义最大最小整数值
- 直流负载线和交流负载线静态工作点分析,与非线性失真类型分析
- 图片裁剪cropperjs的使用
- Java篇第三回——运算符、表达式与语句(C不好的也快来)