内存分配掩码(gfp_mask) - 内存域修饰符 & 内存分配标志

本文内容均基于内核版本Linux-v3.2.40。

    先说说GFP吧,曾几何时,每次写代码遇到kmalloc、alloc_page时都会疑惑,到底是GFP_KERNEL还是GPF_KERNEL,一直搞不清楚,每次都要先搜个例子看看,到后来才知道,原来 GFP是get free page的缩写! 现在想想,都快要被以前的自己蠢哭了,^_^

    好了,切入正题,本文主要讲解内存分配中的分配掩码。分配掩码包括两部分,内存域修饰符(占低4位)和内存分配标志(从第5位开始),如下图所示:

内存分配掩码(gfp_mask) - 内存域修饰符 & 内存分配标志_第1张图片

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
这里主要想说明的是,内存域修饰符与伙伴系统分配器扫描内存域的顺序的关系,如下所示:

内存分配掩码(gfp_mask) - 内存域修饰符 & 内存分配标志_第2张图片

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)

    以上就是与内存分配掩码相关的内容,如有错误欢迎指正。



你可能感兴趣的:(Linux内存管理)