gfp_mask转换成对应的zone和migratetype

本文深入探讨Linux内核中内存分配的三大关键步骤:node的转换、node的选择及migratetype的转换。解析了如何通过gfp_mask确定内存分配的zone,以及在内存不足时如何选择node进行分配。同时,详细介绍了gfp_flags如何转换为migratetype,为理解Linux内核内存管理提供了重要视角。

1.node的转换

在分配内存时,都会带分配参数比如GPF_KERNEL等等,那么,一次内存分配从哪个zone分配了?

这里就必需把mask转换成zone,gfp_mask低4位用于表示分配的zone

static inline enum zone_type gfp_zone(gfp_t flags)
{
    enum zone_type z;
    int bit = (__force int) (flags & GFP_ZONEMASK);

    z = (GFP_ZONE_TABLE >> (bit * ZONES_SHIFT)) &
                     ((1 << ZONES_SHIFT) - 1);
    VM_BUG_ON((GFP_ZONE_BAD >> bit) & 1);
    return z;
}

其中ZONES_SHIFT,表示系统中的zone个数不超过2的ZONE_SHIFT次方,比如zone个数为5,那么zone_SHIFT=3,用

3bit表示zone的index(0-5),如果只有三个zone,那么ZONES_SHIFT=2,因为2个bit,可以表示4个数.

GFP_ZONEMASK定义的地方如下:
#define GFP_ZONEMASK    (__GFP_DMA|__GFP_HIGHMEM|__GFP_DMA32|__GFP_MOVABLE)

#ifdef CONFIG_HIGHMEM
#define OPT_ZONE_HIGHMEM ZONE_HIGHMEM
#else
#define OPT_ZONE_HIGHMEM ZONE_NORMAL
#endif

#ifdef CONFIG_ZONE_DMA
#define OPT_ZONE_DMA ZONE_DMA
#else
#define OPT_ZONE_DMA ZONE_NORMAL
#endif

#ifdef CONFIG_ZONE_DMA32
#define OPT_ZONE_DMA32 ZONE_DMA32
#else
#define OPT_ZONE_DMA32 ZONE_NORMAL
#endif

从其定义可以看出,如果没有定义HIGHMEM,则ZONE_HIGHMEM自动指向ZONE_NORMAL,其他类似。GFP_ZONE_TABLE的定义,其实是将各个ZONE的值放到对应的BIT位上,ZONE_NORMAL放到[0,ZONE_SHIFT)位,GFP_DMA放到[ZONE_SHIFT, 2*ZONE_SHIFT)位上,依此类推。

bit*ZONE_SHIFT即得到该ZONE所对应的BIT位,然后将GFP_ZONE_TABLE左移所得位数量,这时候得到的是从这个ZONE开始往上的所有BIT位,要将无关的ZONE清零,即只取低ZONE_SHIFT位,所以要&((1<<ZONE_SHITF) -1)

2.node的选择

  在进行内存分配时,需要选择从哪个node分配内存,或者当前node内存不足时,又应该从哪个node分配了?

通过gfp_zone(gfp_mask),确认一个high zoneidx(最高的zone index),分配内存的优先级从high zoneidx-> low zone index

ZONE_MOVEBLE -> ZONE_NORMAL->ZONE_DMA,也就是ZONE_MOVEBLE不足时,可以从NORMAL和DMA Zone依次分配.列如GFP_KERNEL,优先从ZONE_NORMAL,不足时从ZONE_DMA分配,而如果标识GFP_DMA,那么只能从ZONE_DMA区分配,因为DMA ZONE的index=0,为最低.

选择zone的代码:

#define for_each_zone_zonelist_nodemask(zone, z, zlist, highidx, nodemask) \
    for (z = first_zones_zonelist(zlist, highidx, nodemask, &zone);    \
        zone;                            \
        z = next_zones_zonelist(++z, highidx, nodemask),    \
            zone = zonelist_zone(z))   

static inline struct zoneref *first_zones_zonelist(struct zonelist *zonelist,
                    enum zone_type highest_zoneidx,
                    nodemask_t *nodes,
                    struct zone **zone)
{
    struct zoneref *z = next_zones_zonelist(zonelist->_zonerefs,
                            highest_zoneidx, nodes);
    *zone = zonelist_zone(z);
    return z;
}

struct zoneref *next_zones_zonelist(struct zoneref *z,
                    enum zone_type highest_zoneidx,    nodemask_t *nodes)
{    
    if (likely(nodes == NULL))
       while (zonelist_zone_idx(z) > highest_zoneidx)//找到第一个合适的zone idx
            z++;
    return z;
}

 

 

3. migratetype的转换

static inline int gfpflags_to_migratetype(const gfp_t gfp_flags)
{
    VM_WARN_ON((gfp_flags & GFP_MOVABLE_MASK) == GFP_MOVABLE_MASK);
    BUILD_BUG_ON((1UL << GFP_MOVABLE_SHIFT) != ___GFP_MOVABLE);
    BUILD_BUG_ON((___GFP_MOVABLE >> GFP_MOVABLE_SHIFT) != MIGRATE_MOVABLE);

    if (unlikely(page_group_by_mobility_disabled))
        return MIGRATE_UNMOVABLE;

    /* Group based on mobility */
    return (gfp_flags & GFP_MOVABLE_MASK) >> GFP_MOVABLE_SHIFT;
}

GFP_MOVABLE_MASK=0x18,GFP_MOVABLE_SHIFT=3,

可以看出,计算migrate type很简单,(gfp_flags & GFP_MOVABLE_MASK) >> GFP_MOVABLE_SHIFT

可以看出gfp_flags的bit3-bit4表示migrate类型

enum {
    MIGRATE_UNMOVABLE,
    MIGRATE_MOVABLE,
    MIGRATE_RECLAIMABLE,
    MIGRATE_PCPTYPES,    /* the number of types on the pcp lists */
    MIGRATE_HIGHATOMIC = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
    MIGRATE_CMA,
#endif
#ifdef CONFIG_MEMORY_ISOLATION
    MIGRATE_ISOLATE,    /* can't allocate from here */
#endif
    MIGRATE_TYPES
};

gfp_mask(get free page)是在申请内存时传递的一个标记位,里面包含区域修饰符、行为修饰符、类型修饰符等信息,用于控制内存分配的行为方式[^2]。 ### 定义与构成 gfp_mask是一个掩码值,通过不同的位组合来表示不同的分配需求。例如,0X201da = 0x20000 | 0x100| 0x80 | 0x40 | 0x10 | 0x08 | 0x02 ,分别对应___GFP_HARDWAL、___GFP_COLD、___GFP_FS、___GFP_IO、___GFP_MOVABLE、___GFP_HIGHMEM等不同的标志位[^2]。 ### 作用 gfp_mask的主要作用是向内存分配器传达内存分配的具体要求,包括是否允许页面回收、是否允许访问系统预留内存、分配的内存是否马上使用等,从而让内存分配器根据这些要求进行内存分配操作。例如,__GFP_HIGH表示分配内存具有高优先级,并且这个分配请求是很有必要的,分配器可以使用紧急的内存池;__GFP_ATOMIC表示分配内存的过程不能执行页面回收或者睡眠动作,并且具有很高的优先级,常用于中断上下文分配内存[^3]。 ### 常见标志位及含义 - **水位修饰符**:用来控制是否可以访问系统紧急预留的内存,如__GFP_HIGH、__GFP_ATOMIC、__GFP_MEMALLOC、__GFP_NOMEMALLOC等[^3]。 - **页面回收修饰符**:控制页面回收机制的使用,如__GFP_IO、__GFP_FS、__GFP_DIRECT_RECLAIM、__GFP_KSWAPD_RECLAIM、__GFP_RECLAIM等[^3]。 - **行动修饰符**:描述分配内存的一些行为,如__GFP_COLD、__GFP_NOWARN、__GFP_ZERO、__GFP_NOTRACK、__GFP_OTHER_NODE等[^3]。 ### 使用方法 在进行内存分配时,将gfp_mask作为参数传递给相应的内存分配函数。例如,在调用页面分配接口函数时,根据具体的分配需求设置gfp_mask的值,以满足不同场景下的内存分配要求。当分配者希望分配内存不失败时,可使用__GFP_NOFAIL标志位,而不是自己写一个while循环来不断调用页面分配接口函数[^3]。 ### 代码示例 以下是一个简单的伪代码示例,展示如何使用gfp_mask进行内存分配: ```c #include <linux/gfp.h> // 定义gfp_mask gfp_t gfp_mask = __GFP_HIGH | __GFP_ZERO; // 调用内存分配函数,这里假设存在一个名为alloc_pages的函数 struct page *page = alloc_pages(gfp_mask, order); if (page != NULL) { // 内存分配成功,进行后续操作 } else { // 内存分配失败,处理错误 } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值