本文内容均基于内核版本Linux-v3.2.40。
先说说GFP吧,曾几何时,每次写代码遇到kmalloc、alloc_page时都会疑惑,到底是GFP_KERNEL还是GPF_KERNEL,一直搞不清楚,每次都要先搜个例子看看,到后来才知道,原来GFP是get free page的缩写!现在想想,都快要被以前的自己蠢哭了,^_^
好了,切入正题,本文主要讲解内存分配中的分配掩码。分配掩码包括两部分,内存域修饰符(占低4位)和内存分配标志(从第5位开始),如下图所示:
1. 内存域修饰符
前面的文章已经介绍过内存域zone的几种类型:ZONE_DMA、
ZONE_DMA32、
ZONE_NORMAL、
ZONE_HIGHMEM、
ZONE_MOVABLE。与类型不同,内存域的修饰符只有___GFP_DMA、___GFP_HIGHMEM、___GFP_DMA32、___GFP_MOVABLE 4种,没有ZONE_NORMAL对应的修饰符,因为ZONE_NORMAL是默认的内存申请类型。如下所示,为内存域修饰符的定义:
-
#define ___GFP_DMA 0x01u
-
#define ___GFP_HIGHMEM 0x02u
-
#define ___GFP_DMA32 0x04u
-
#define ___GFP_MOVABLE 0x08u
这里主要想说明的是,内存域修饰符与伙伴系统分配器扫描内存域的顺序的关系,如下所示:
2. 内存分配标志
除了内存域修饰符之外,分配掩码中还包含了大量的分配标志,如下所示:
-
#define __GFP_WAIT ((__force gfp_t)___GFP_WAIT) /* 内存分配的过程中可以被打断 */
-
#define __GFP_HIGH ((__force gfp_t)___GFP_HIGH) /* 请求分配非常紧急的内存,注意与__GFP_HIGHMEM的区分,__GPF_HIGHMEM指从高端内存域分配内存 */
-
#define __GFP_IO ((__force gfp_t)___GFP_IO) /* 内存分配的过程中可进行IO操作,也就是说分配过程中如果需要换出页,必须设置该标志,才能将换出的页写入磁盘 */
-
#define __GFP_FS ((__force gfp_t)___GFP_FS) /* 内存分配过程中可执行VFS操作,也就是可以调用VFS的接口 */
-
#define __GFP_COLD ((__force gfp_t)___GFP_COLD) /* 分配不在cpu高速缓存中的冷页 */
-
-
#define __GFP_NOWARN ((__force gfp_t)___GFP_NOWARN) /* 内存分配时不允许内核发出警告,极少使用 */
-
#define __GFP_REPEAT ((__force gfp_t)___GFP_REPEAT) /* 内存分配失败后,会进行重试,重试若干次后停止 */
-
#define __GFP_NOFAIL ((__force gfp_t)___GFP_NOFAIL) /* 内存分配失败后一直重试,直至成功 */
-
#define __GFP_NORETRY ((__force gfp_t)___GFP_NORETRY) /* 内存分配失败后,不进行重试 */
-
-
#define __GFP_COMP ((__force gfp_t)___GFP_COMP) /* 增加复合元数据 */
-
#define __GFP_ZERO ((__force gfp_t)___GFP_ZERO) /* 申请全部填充为0的page */
-
-
#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) /* 不使用紧急分配链表 */
-
#define __GFP_HARDWALL ((__force gfp_t)___GFP_HARDWALL) /* 只能在当前进程可运行的cpu关联的内存节点上分配内存,如果进程可在所有cpu上运行,该标志无意义 */
-
#define __GFP_THISNODE ((__force gfp_t)___GFP_THISNODE) /* 只能在当前节点上分配内存 */
-
#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* 请求分配可回收的page */
-
#define __GFP_NOTRACK ((__force gfp_t)___GFP_NOTRACK) /* 不对分配的内存进行跟踪 */
-
-
#define __GFP_NO_KSWAPD ((__force gfp_t)___GFP_NO_KSWAPD)
-
#define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */
由于这些标志几乎总是组合使用,内核中作了一些分组,包含了用于各种标准情形的适当标志,如下所示:
-
/* This equals 0, but use constants in case they ever change */
-
#define GFP_NOWAIT (GFP_ATOMIC & ~__GFP_HIGH)
-
/* GFP_ATOMIC means both !wait (__GFP_WAIT not set) and use emergency pool */
-
#define GFP_ATOMIC (__GFP_HIGH) /* 表示申请内存非常紧急,不能睡眠,不能有IO和VFS操作 */
-
#define GFP_NOIO (__GFP_WAIT)
-
#define GFP_NOFS (__GFP_WAIT | __GFP_IO)
-
#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS) /* 可以睡眠,可以有IO和VFS操作 */
-
#define GFP_TEMPORARY (__GFP_WAIT | __GFP_IO | __GFP_FS | \
-
__GFP_RECLAIMABLE)
-
-
/* 以下三个用于为用户空间申请内存 */
-
#define GFP_USER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL) /* 可以睡眠,可以有IO和VFS操作,只能从进程可运行的node上分配内存 */
-
#define GFP_HIGHUSER (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HARDWALL | __GFP_HIGHMEM) /* 优先从高端zone中分配内存 */
-
#define GFP_HIGHUSER_MOVABLE (__GFP_WAIT | __GFP_IO | __GFP_FS | \
-
__GFP_HARDWALL | __GFP_HIGHMEM | __GFP_MOVABLE) /* 申请可移动的内存 */
-
-
#define GFP_IOFS (__GFP_IO | __GFP_FS)
-
#define GFP_TRANSHUGE (GFP_HIGHUSER_MOVABLE | __GFP_COMP | \
-
__GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN | \
-
__GFP_NO_KSWAPD)
3. 常见的几种内存分配场景及使用的标志
1) 缺页异常分配内存时:GFP_HIGHUSER | __GFP_ZERO | __GFP_MOVABLE /* 分配可移动的page,并且将page清零 */
do_page_fault() -> handle_pte_fault() -> do_anonymous_page() -> alloc_zeroed_user_highpage_movable()
2) 文件映射分配内存时:GFP_HIGHUSER_MOVABLE /* 分配可移动的page */
do_page_fault() -> handle_pte_fault() -> do_anonymous_page() -> do_nonlinear_fault() -> __do_fault()
3) vmalloc分配内存时:GFP_KERNEL | __GFP_HIGHMEM /* 优先从高端内存中分配 */
vmalloc() -> __vmalloc_node_flags(size, -1, GFP_KERNEL | __GFP_HIGHMEM)
以上就是与内存分配掩码相关的内容,如有错误欢迎指正。
阅读(10842) | 评论(0) | 转发(0) |