首页 > 系统相关 >内存管理-33-GFP内存分配标志

内存管理-33-GFP内存分配标志

时间:2024-09-02 18:24:57浏览次数:12  
标签:__ ALLOC 33 GFP 内存 ___ gfp define

基于msm-5.4


一、GFP分配标志

注: 这些标志为1表示功能是启用的。

1. 最初的纯整数 GFP 位掩码(不要直接使用)

//include/linux/gfp.h
#define ___GFP_DMA        0x01u
#define ___GFP_HIGHMEM        0x02u
#define ___GFP_DMA32        0x04u
#define ___GFP_MOVABLE        0x08u
#define ___GFP_RECLAIMABLE    0x10u
#define ___GFP_HIGH        0x20u
#define ___GFP_IO        0x40u
#define ___GFP_FS        0x80u
#define ___GFP_ZERO        0x100u
#define ___GFP_ATOMIC        0x200u
#define ___GFP_DIRECT_RECLAIM    0x400u
#define ___GFP_KSWAPD_RECLAIM    0x800u
#define ___GFP_WRITE        0x1000u
#define ___GFP_NOWARN        0x2000u
#define ___GFP_RETRY_MAYFAIL    0x4000u
#define ___GFP_NOFAIL        0x8000u
#define ___GFP_NORETRY        0x10000u
#define ___GFP_MEMALLOC        0x20000u
#define ___GFP_COMP        0x40000u
#define ___GFP_NOMEMALLOC    0x80000u
#define ___GFP_HARDWALL        0x100000u
#define ___GFP_THISNODE        0x200000u
#define ___GFP_ACCOUNT        0x400000u
#ifdef CONFIG_LOCKDEP
#define ___GFP_NOLOCKDEP    0x800000u
#else
#define ___GFP_NOLOCKDEP    0
#endif
#define ___GFP_CMA        0x1000000u
#ifdef CONFIG_LIMIT_MOVABLE_ZONE_ALLOC
#define ___GFP_OFFLINABLE    0x2000000u
#else
#define ___GFP_OFFLINABLE    0
#endif

这是最初的纯整数 GFP 位掩码,请勿直接使用。若是要修改这里的掩码,需要和 trace/events/mmflags.h 和 tools/perf/builtin-kmem.c 保持一致。


2. 物理地址区域修饰符

#define __GFP_DMA    ((__force gfp_t)___GFP_DMA) //bit1
#define __GFP_HIGHMEM    ((__force gfp_t)___GFP_HIGHMEM) //bit2
#define __GFP_DMA32    ((__force gfp_t)___GFP_DMA32) //bit4
#define __GFP_MOVABLE    ((__force gfp_t)___GFP_MOVABLE)  //bit8 /* ZONE_MOVABLE allowed */
#define __GFP_CMA    ((__force gfp_t)___GFP_CMA) //0x1000000u = bit24
#define __GFP_OFFLINABLE    ((__force gfp_t)___GFP_OFFLINABLE) //bit25 或 0
#define GFP_ZONEMASK    (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE) //0xf


3. 页面移动性和放置提示

#define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) //0x10
#define __GFP_WRITE    ((__force gfp_t)___GFP_WRITE) //0x1000u
#define __GFP_HARDWALL   ((__force gfp_t)___GFP_HARDWALL) //0x100000u
#define __GFP_THISNODE    ((__force gfp_t)___GFP_THISNODE) //0x200000u
#define __GFP_ACCOUNT    ((__force gfp_t)___GFP_ACCOUNT) //0x400000u

这些标志提供有关页面移动性的提示。具有类似移动性的页面放置在相同的页面块内,以尽量减少由于内存外部碎片化引起的问题。

各标志含义:

_GFP_MOVABLE: 也是区域修饰符, 表示页面可以在内存压缩期间通过页面迁移移动或可以回收。

__GFP_RECLAIMABLE: 用于指定 SLAB_RECLAIM_ACCOUNT 的 slab 分配,其页面可以通过shrinkers释放。

__GFP_WRITE: 表示调用者打算弄脏页面。在可能的情况下,这些页面将分布在本地zones之间,以避免所有脏页面都在一个区域中(公平区域分配策略)。

__GFP_HARDWALL: 强制执行 cpuset 内存分配策略。

__GFP_THISNODE: 强制从请求的节点满足分配,不进行任何回退或实施放置策略。

__GFP_ACCOUNT: 导致将分配记入 kmemcg。


4. 水位修饰符

#define __GFP_ATOMIC    ((__force gfp_t)___GFP_ATOMIC) //0x200
#define __GFP_HIGH    ((__force gfp_t)___GFP_HIGH) //0x20
#define __GFP_MEMALLOC    ((__force gfp_t)___GFP_MEMALLOC) //0x20000
#define __GFP_NOMEMALLOC ((__force gfp_t)___GFP_NOMEMALLOC) //0x80000

水位修饰符控制对针对紧急情况而预留的内存的访问。

各标志含义:

__GFP_HIGH: 表示调用者具有高优先级,并且必须先授予请求,系统才能继续前进。例如,创建 IO 上下文来清理页面。

__GFP_ATOMIC: 表示调用者上下文无法执行回收或休眠,并且具有高优先级。用户通常是中断处理程序。这可以与 __GFP_HIGH 结合使用

__GFP_MEMALLOC: 允许访问所有内存。这应该只在调用者保证分配是为了允许在很短的时间内释放更多内存时使用,例如进程退出或交换。用户应该是 MM 或与 VM 密切协调(例如通过 NFS 交换)。

__GFP_NOMEMALLOC: 用于明确禁止访问紧急预留的那部分内存。如果两者都设置,则该标志优先于 __GFP_MEMALLOC 标志。


5. 回收修饰符

#define __GFP_IO    ((__force gfp_t)___GFP_IO) //0x40
#define __GFP_FS    ((__force gfp_t)___GFP_FS) //0x80u
#define __GFP_DIRECT_RECLAIM    ((__force gfp_t)___GFP_DIRECT_RECLAIM) //0x400u  /* Caller can reclaim */
#define __GFP_KSWAPD_RECLAIM    ((__force gfp_t)___GFP_KSWAPD_RECLAIM) //0x800u  /* kswapd can wake */
#define __GFP_RECLAIM ((__force gfp_t)(___GFP_DIRECT_RECLAIM|___GFP_KSWAPD_RECLAIM)) //0x400|0x800=0xc00u
#define __GFP_RETRY_MAYFAIL    ((__force gfp_t)___GFP_RETRY_MAYFAIL) //0x4000u
#define __GFP_NOFAIL    ((__force gfp_t)___GFP_NOFAIL) //0x8000u
#define __GFP_NORETRY    ((__force gfp_t)___GFP_NORETRY) //0x10000u

各标志含义:

__GFP_IO: 可以启动物理 IO。

__GFP_FS: 可以向下调用到低级 FS。清除该标志可避免分配器递归到可能已持有锁的文件系统。

__GFP_DIRECT_RECLAIM: 表示调用者可以进入直接内存回收(direct reclaim)。当有后备选项可用时,可以清除此标志以避免不必要的延迟。

__GFP_KSWAPD_RECLAIM: 表示调用者希望在达到低水位时唤醒 kswapd,并让其回收页面,直到达到高水位。当有后备选项可用且回收可能会破坏系统时,调用者可能希望清除此标志。典型示例是 THP(Transparent HugePages) 分配,其中后备成本低廉,但回收/压缩可能会导致间接卡住。

__GFP_RECLAIM: 是允许/禁止直接内存回收 和 kswapd回收的简写。

默认分配行为取决于请求分配的大小。我们有一个所谓的昂贵分配的概念, 即 order > PAGE_ALLOC_COSTLY_ORDER(3)。!costly 分配太重要而不能失败,因此默认情况下它们隐式地不会失败(有一些例外,例如, OOM受害者(OOM victims)可能会失败,因此调用者仍然必须检查失败),而昂贵的请求试图不造成破坏,甚至在不调用 OOM 杀手的情况下也会退出。以下三个修饰符可用于覆盖其中一些隐式规则:

__GFP_NORETRY: VM 实现将仅尝试非常轻量的内存直接回收,以便在内存压力下获得一些内存(因此它可能会休眠)。它将避免像 OOM 杀手这样的破坏性操作。调用者必须处理在重度内存压力下很可能发生的故障。当故障可以以较小的代价轻松处理时,例如降低吞吐量,该标志是合适的。

__GFP_RETRY_MAYFAIL: 如果有迹象表明其他地方已经取得进展,VM 实现将重试之前失败的内存回收过程。它可以等待其他任务尝试释放内存的高级方法,例如压缩(消除碎片)和页面换出(page-out)。重试次数仍然有一定的限制,但比 __GFP_NORETRY 的限制要大。
使用此标志的分配可能会失败,但只有当未使用的内存确实很少时才会失败。虽然这些分配不会直接触发 OOM 终止程序,但它们的失败表明系统很可能需要尽快使用 OOM 终止程序。调用者必须处理失败,但可以合理地通过使更高级别的请求失败或仅以效率低得多的方式完成它来做到这一点。
如果分配确实失败了,并且调用者能够释放一些非必要的内存,那么这样做可能会使整个系统受益。

__GFP_NOFAIL: VM 实现必须无限重试:调用者无法处理分配失败。分配可能会无限期阻塞,但永远不会返回失败。因此检测失败是没有意义的。
新用户需要被仔细评估(并且仅在没有合理的失败策略时才应使用该标志),但使用该标志绝对比围绕分配器进行开放式代码无限循环更好。
强烈不建议将此标志用于昂贵的分配,即 order > PAGE_ALLOC_COSTLY_ORDER(3)。。


6. 操作修饰符

#define __GFP_NOWARN    ((__force gfp_t)___GFP_NOWARN) //0x2000u
#define __GFP_COMP    ((__force gfp_t)___GFP_COMP) //0x40000u
#define __GFP_ZERO    ((__force gfp_t)___GFP_ZERO) //0x100u

各标志含义:

__GFP_NOWARN: 抑制分配失败报告。

__GFP_COMP: 地址复合页面元数据。

__GFP_ZERO: 在成功时返回零页面。


7. 有用的 GFP 标志组合

#define GFP_ATOMIC    (__GFP_HIGH|__GFP_ATOMIC|__GFP_KSWAPD_RECLAIM) //0x20|0x200|0x800=0xa20
#define GFP_KERNEL    (__GFP_RECLAIM | __GFP_IO | __GFP_FS) //0xc00|0x40|0x80=0xcc0
#define GFP_KERNEL_ACCOUNT (GFP_KERNEL | __GFP_ACCOUNT) //0xcc0|0x400000=0x400cc0
#define GFP_NOWAIT    (__GFP_KSWAPD_RECLAIM) //0x800
#define GFP_NOIO    (__GFP_RECLAIM) //0xc00
#define GFP_NOFS    (__GFP_RECLAIM | __GFP_IO) //0xc00|0x40=0xc40
#define GFP_USER    (__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL) //0xc00|0x40|0x80|0x100000=0x100cc0
#define GFP_DMA        __GFP_DMA //0x01
#define GFP_DMA32    __GFP_DMA32 //0x04
#define GFP_HIGHUSER    (GFP_USER | __GFP_HIGHMEM) //0x100cc0|0x02=0x100cc2
#define GFP_HIGHUSER_MOVABLE    (GFP_HIGHUSER | __GFP_MOVABLE) //0x100cc2|0x08=0x100cca
#define GFP_TRANSHUGE_LIGHT    ((GFP_HIGHUSER_MOVABLE | __GFP_COMP |  __GFP_NOMEMALLOC | __GFP_NOWARN) & ~__GFP_RECLAIM) //(0x100cca|0x40000|0x80000|0x2000)&~0xc00=0x1c20ca
#define GFP_TRANSHUGE    (GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM) //0x1c20ca|0x400=0x1c24ca

常用的有用 GFP 标志组合。建议子系统从这些组合之一开始,然后根据需要设置/清除 %__GFP_FOO 标志。

各标志含义:

GFP_ATOMIC: 用户不能休眠,需要能成功分配。应用较低的水印以允许访问“原子保留内存”

GFP_KERNEL: 是内核内部分配的典型代表。调用者需要 %ZONE_NORMAL 或较低的区域才能直接访问,但可以直接回收。

GFP_KERNEL_ACCOUNT: 与 GFP_KERNEL 相同,只是分配要记入 kmemcg。

GFP_NOWAIT: 用于不应因直接回收而停滞、启动物理 IO 或使用任何文件系统回调的内核分配。

GFP_NOIO: 将使用直接回收来丢弃不需要启动任何物理 IO 的干净页面或 slab 页面。请尽量避免直接使用此标志,而是使用 memalloc_noio_save()/memalloc_noio_restore() 来标记无法执行任何 IO 的整个范围,并简要说明原因。所有分配请求都将隐式继承 GFP_NOIO。

GFP_NOFS: 将使用直接回收,但不会使用任何文件系统接口。请尽量避免直接使用此标志,而应使用 memalloc_nofs_save()/memalloc_nofs_restore() 来标记整个范围,该范围不能/不应递归到 FS 层,并简要说明原因。所有分配请求都将隐式继承 GFP_NOFS。

GFP_USER: 用于用户空间分配,但这些分配的内存也需要由内核或硬件直接访问。它通常由硬件用于映射到用户空间(例如图形)的缓冲区,硬件仍必须通过 DMA 访问。这些分配强制执行 cpuset 限制。

GFP_DMA: 的存在是出于历史原因,应尽可能避免。这些标志表示调用者要求使用最低区域(%ZONE_DMA 或 x86-64 上的 16M)。理想情况下,它会被删除,但它需要仔细审核,因为有些用户确实需要它,而其他用户使用该标志来避免 %ZONE_DMA 中的低内存保留,并将最低区域视为一种紧急保留。

GFP_DMA32 与 GFP_DMA 类似,只是调用者需要 32 位地址。

GFP_HIGHUSER: 用于可能映射到用户空间的用户空间的分配,不需要由内核直接访问,但在使用时不能移动。一个例子可能是将数据直接映射到用户空间但没有寻址限制的硬件分配。

GFP_HIGHUSER_MOVABLE: 用于内核不需要直接访问但在需要访问时可以使用 kmap() 的用户空间分配。它们应该可以通过页面回收或页面迁移来移动。通常,LRU 上的页面也会使用 %GFP_HIGHUSER_MOVABLE 进行分配。

GFP_TRANSHUGE 和 %GFP_TRANSHUGE_LIGHT 用于 THP 分配。它们是复合分配,如果内存不可用,通常会很快失败,并且不会在失败时唤醒 kswapd/kcompactd。_LIGHT 版本根本不尝试回收/压缩,并且默认用于page fault路径,而 non-light 版本由 khugepaged 使用。


二、ALLOC内部标志

1. 标志定义

//mm/internal.h
/* The ALLOC_WMARK bits are used as an index to zone->watermark */
#define ALLOC_WMARK_MIN        WMARK_MIN  //0
#define ALLOC_WMARK_LOW        WMARK_LOW  //1
#define ALLOC_WMARK_HIGH    WMARK_HIGH //2
#define ALLOC_NO_WATERMARKS    0x04 /* don't check watermarks at all */
/* Mask to get the watermark bits */
#define ALLOC_WMARK_MASK    (ALLOC_NO_WATERMARKS-1) //3
#define ALLOC_OOM    0x08
#define ALLOC_HARDER         0x10 /* try to alloc harder */
#define ALLOC_HIGH         0x20 /* __GFP_HIGH set */
#define ALLOC_CPUSET         0x40 /* check for correct cpuset */
#define ALLOC_CMA         0x80 /* allow allocations from CMA areas */
#define ALLOC_NOFRAGMENT      0x0
#define ALLOC_KSWAPD        0x200 /* allow waking of kswapd */

各标志含义:

ALLOC_WMARK_MIN: 仅在最小水位water mark及以上限制页面分配;
ALLOC_WMARK_LOW: 仅在低水位water mark及以上限制页面分配;
ALLOC_WMARK_HIGH: 仅在高水位water mark及以上限制页面分配;
ALLOC_NO_WATERMARKS: 页面分配时不检查水位
ALLOC_HARDER: 尽力分配,一般在gfp_mask设置了 __GFP_ATOMIC 时会使用。如果页面分配失败,则尽可能分配 MIGRATE_HIGHATOMIC 类型的空闲页面。
ALLOC_HIGH: 高优先级分配,一般在gfp_mask设置了 __GFP_HIGH 时使用.
ALLOC_CPUSET: 检查是否为正确的cpuset.
ALLOC_CMA: 允许从CMA区域进行分配.
ALLOC_KSWAPD: 内存不足时唤醒kswapd内核线程.


三、相关辅助函数

1. gfp_to_alloc_flags()

根据 gfp_mask 对内存分配标识进行调整。

static inline unsigned int gfp_to_alloc_flags(gfp_t gfp_mask) //page_alloc.c
{
    /* (1) 默认值 */
    unsigned int alloc_flags = ALLOC_WMARK_MIN | ALLOC_CPUSET;

    /* (2) 若指定了 __GFP_HIGH 标志,就将其转换为 ALLOC_HIGH 标志 */
    alloc_flags |= (__force int) (gfp_mask & __GFP_HIGH);

    /* 
     * (3) 在指定GFP_ATOMIC的情况下,如果没有明确禁止访问紧急预留的那部分内存,则或上 ALLOC_HARDER 标志.
     *   (3.1) 在指定GFP_ATOMIC的情况下,强制清除 ALLOC_CPUSET 标志。
     * (4) 在没有指定 __GFP_ATOMIC 的情况下, 若当前是RT线程且不在中断上下文中,则强制或上 ALLOC_HARDER.
     */
    if (gfp_mask & __GFP_ATOMIC) {
        if (!(gfp_mask & __GFP_NOMEMALLOC)) //__GFP_NOMEMALLOC 用于明确禁止访问紧急预留的那部分内存
            alloc_flags |= ALLOC_HARDER;
        alloc_flags &= ~ALLOC_CPUSET;
    } else if (unlikely(rt_task(current)) && !in_interrupt()) //对RT线程有特殊处理
        alloc_flags |= ALLOC_HARDER;

    /* (5) 同步使能kswapd异步回收标志 */
    if (gfp_mask & __GFP_KSWAPD_RECLAIM)
        alloc_flags |= ALLOC_KSWAPD;

#ifdef CONFIG_CMA
    /* (6) 如果指定了 __GFP_MOVABLE 且指定了 __GFP_CMA,则或上 ALLOC_CMA 标志 */
    if ((gfpflags_to_migratetype(gfp_mask) == MIGRATE_MOVABLE) && (gfp_mask & __GFP_CMA)) //1 0x1000000
        alloc_flags |= ALLOC_CMA;
#endif
    return alloc_flags;
}


2. 涉及 current->flags 设置的分配

memalloc_noio_save()/memalloc_noio_restore() 设置 PF_MEMALLOC_NOIO 标志;memalloc_nofs_save()/memalloc_nofs_restore() 设置 PF_MEMALLOC_NOFS 标志。这两个分别标记 ~(__GFP_IO | __GFP_FS) 和 ~__GFP_IO 的区间。整个区间内分配的内存都会防止递归到文件系统或IO中,以防死锁。

static inline gfp_t current_gfp_context(gfp_t flags) //include/linux/sched/mm.h
{
    if (unlikely(current->flags & (PF_MEMALLOC_NOIO | PF_MEMALLOC_NOFS | PF_MEMALLOC_NOCMA))) {
        /* NOIO 意味着 NOIO 和 NOFS,并且它是一个较弱的上下文,因此始终确保它优先 */
        if (current->flags & PF_MEMALLOC_NOIO)
            flags &= ~(__GFP_IO | __GFP_FS);
        else if (current->flags & PF_MEMALLOC_NOFS)
            flags &= ~__GFP_FS;
#ifdef CONFIG_CMA
        if (current->flags & PF_MEMALLOC_NOCMA) //PF_MEMALLOC_NOCMA: 表示不要从CMA区域分配内存
            flags &= ~__GFP_MOVABLE;
#endif
    }
    return flags;
}

其调用路径:

__alloc_pages_slowpath //page_alloc.c 内存分配慢速路径
    __alloc_pages_direct_reclaim //page_alloc.c
        __perform_reclaim //page_alloc.c
            try_to_free_pages //vmsacn.c
        reclaim_high //memcontrol.c
        try_charge //memcontrol.c
        mem_cgroup_resize_max //memcontrol.c
        mem_cgroup_force_empty //memcontrol.c
        memory_high_write //memcontrol.c
        memory_max_write //memcontrol.c
            try_to_free_mem_cgroup_pages //vmsacn.c
    get_page_from_freelist //page_alloc.c
        node_reclaim //vmsacn.c
            __node_reclaim //vmsacn.c
            __alloc_pages_nodemask //page_alloc.c 默认配置下无调用位置
        cma_alloc //cma.c
            alloc_contig_range //page_alloc.c
        fs_reclaim_acquire
        fs_reclaim_release
            __need_fs_reclaim //page_alloc.c 使能 CONFIG_LOCKDEP 才生效
                current_gfp_context(gfp_mask)

 

标签:__,ALLOC,33,GFP,内存,___,gfp,define
From: https://www.cnblogs.com/hellokitty2/p/18393265

相关文章

  • 【2025考研英语高分写作:20大必备范文】经典范文001 辞职信 P33
    Directions:        TwomonthsagoyougotajobasaneditorforthemagazineDesign&Fashions.Butnowyoufindthattheworkisnotwhatyouexpected.Youdecidetoquit.Writealettertoyourboss,Mr.Wang,tellinghimyourdecision,stating......
  • OBCE 第三章实验 内存管理手动实践 深入了解Queuing(buffer)表
    实验环境:oceanbase企业版V31-1-1架构。1.查看当前资源情况selectunit_config_id,name,max_cpu,min_cpu,round(max_memory/1024/1024/1024)max_mem_gb,round(min_memory/1024/1024/1024)min_mem_gb,round(max_disk_size/1024/1024/1024)max_disk_size_gbfrom__......
  • 33 CONST
    CONSTReference:CONSTinc++Ifyoudecalresomethingwithconst,you'resayingthatyouarenotgoingtomodifythatthing.Combningwithpointersconstint*a=newint;//orintconst*a=newint;//theymeanthatyoucannotmodifytheco......
  • 电子秤方案的主控芯片选型SIC8833
     电子秤的方案设计功能比较简单,四个压力模块和ADC芯片以及再加个主控芯片大约就构成了其核心功能的器件要求。ADC芯片的功能是将压力模块所得到压力值转化为可显示的数值,在通过LED或者LCD屏展现出来,就是后面我们测量物品或体重所得到的重量斤数。 主控芯片就是MCU,它是方案的......
  • tomcat内存马
    Tomcat内存马学习内存马主要分为以下几类:servlet-api类filter型servlet型spring类拦截器controller型JavaInstrumentation类agent型Tomcat环境搭建按照教程来就行了参考:https://www.cnblogs.com/bktown/p/17636156.html#%E4%B8%8B%E8%BD%BDtomcatmaven项目的t......
  • 33. 指针和函数
    1函数形参改变实参的值#include<stdio.h>voidswap1(intx,inty){ inttmp; tmp=x; x=y; y=tmp; printf("x=%d,y=%d\n",x,y);}voidswap2(int*x,int*y){ inttmp; tmp=*x; *x=*y; *y=tmp;}intmain(){ inta=3; intb=5......
  • 37. 内存布局
    1内存分区C代码经过预处理、编译、汇编、链接4步后生成一个可执行程序。在Windows下,程序是一个普通的可执行文件,以下列出一个二进制可执行文件的基本情况:通过上图可以得知,在没有运行程序前,也就是说程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text)、数据区(da......
  • 38. 内存分区代码分析
    1.返回栈区地址#include<stdio.h>int*fun(){inta=10;return&a;}intmain(intargc,char*argv[]){int*p=NULL;p=fun();*p=100;//操作野指针指向的内存,errreturn0;}2.返回data区地址#include<stdio.h>int*fun(){......
  • 20240905_000339 mysql 存储过程 用户自定义变量
    自定义变量的特点一个@符号定义自定变量打印自定变量另一种定义方式查询赋值......
  • 20240905_010339 mysql 存储过程 局部变量
    ......