kmalloc分配内存大小的限制和宏的一种用法

kmalloc是通过cache来实现的, 只不过每次kmalloc的大小不同, 因此是从不同的cache中分配:

/* include/linux/slab.h */
// 注意kmalloc是在头文件中定义的
static inline void *kmalloc(size_t size, gfp_t flags)
{
    if (__builtin_constant_p(size)) 
    {
        // 以下是找一个对象大小刚好大于等于size的cache
        int i = 0;
        #define CACHE(x) 
            if (size <= x) 
                goto found; 
            else 
                i++;
            #include "kmalloc_sizes.h"
        #undef CACHE
        {
            extern void __you_cannot_kmalloc_that_much(void);
            __you_cannot_kmalloc_that_much();
        }
    found:
        // 实际还是通过kmem_cache_alloc来分配内存空间, 因此也是cache
        return kmem_cache_alloc((flags & GFP_DMA) ? malloc_sizes[i].cs_dmacachep : malloc_sizes[i].cs_cachep, flags);
    }
    // 通过该函数最后也是由__cache_alloc()函数来分配空间
    return __kmalloc(size, flags);
}

kmalloc_sizes.h文件内容, 实际就是定义CACHE中可用的对象大小:

// 普通情况下最大是128K, 也就是kmalloc能分配的最大内存量
#if (PAGE_SIZE == 4096)
     CACHE(32)
#endif
CACHE(64)
#if L1_CACHE_BYTES < 64
     CACHE(96)
#endif
CACHE(128)
#if L1_CACHE_BYTES < 128
    CACHE(192)
#endif
CACHE(256)
CACHE(512)
CACHE(1024)
CACHE(2048)
CACHE(4096)
CACHE(8192)
CACHE(16384)
CACHE(32768)
CACHE(65536)
CACHE(131072)
#if (NR_CPUS > 512) || (MAX_NUMNODES > 256) || !defined(CONFIG_MMU)
    CACHE(262144)
#endif
#ifndef CONFIG_MMU
    CACHE(524288)
    CACHE(1048576)
    #ifdef CONFIG_LARGE_ALLOCS
        CACHE(2097152)
        CACHE(4194304)
        CACHE(8388608)
        CACHE(16777216)
        CACHE(33554432)
    #endif /* CONFIG_LARGE_ALLOCS */
#endif /* CONFIG_MMU

继续调用:

void *__kmalloc(size_t size, gfp_t flags)
{
    return __do_kmalloc(size, flags, NULL);
}


static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, void *caller)
{
    struct kmem_cache *cachep;

    /* If you want to save a few bytes .text space: replace
    * __ with kmem_.
    * Then kmalloc uses the uninlined functions instead of the inline
    * functions.
    */
    cachep = __find_general_cachep(size, flags);
    if (unlikely(cachep == NULL))
        return NULL;
    return __cache_alloc(cachep, flags, caller);
}


static inline struct kmem_cache *__find_general_cachep(size_t size, gfp_t gfpflags)
{
    struct cache_sizes *csizep = malloc_sizes;

    #if DEBUG
        /* This happens if someone tries to call
        * kmem_cache_create(), or __kmalloc(), before
        * the generic caches are initialized.
        */
        BUG_ON(malloc_sizes[INDEX_AC].cs_cachep == NULL);
    #endif

    while (size > csizep->cs_size)
        csizep++;

    /*
    * Really subtle: The last entry with cs->cs_size==ULONG_MAX
    * has cs_{dma,}cachep==NULL. Thus no special case
    * for large kmalloc calls required.
    */
    if (unlikely(gfpflags & GFP_DMA))
        return csizep->cs_dmacachep;

    return csizep->cs_cachep;
}

__find_general_cachep确定可以分配的空间大小由malloc_sizes结构体数组指定,在mm/slab.c中它的定义如下:

struct cache_sizes malloc_sizes[] = {
    #define CACHE(x) { .cs_size = (x) },     // 定义宏
    #include <linux/kmalloc_sizes.h>        // 使用宏
    CACHE(ULONG_MAX)                        // 使用宏
    #undef CACHE                            // 取消宏
};

在对一个结构体进行赋值的时候,第一次看到了这个用法。在slab_def.h中找到cache_sizes的定义如下:

struct cache_sizes {

    size_t cs_size;
    struct kmem_cache *cs_cachep;

    #ifdef CONFIG_ZONE_DMA
        struct kmem_cache *cs_dmacachep;
    #endif
};

可以看出,无论cache_sizes的如何变,至少都要给cs_size分配一个值。函数一就是对malloc_sizes这个数组进行初始化。在初始化的时候却多了3行代码:

  • 代码一:#define CACHE(x) { .cs_size = (x) },

作用:定义一个CACHE(x)的宏,作用在include里面。

  • 代码二:#include <linux/kmalloc_sizes.h>

作用,引用kmalloc_size.h文件。使malloc_sizes[]得到初始化的值。换个角度看,把函数初始化和依据附加条件分开,使得程序的逻辑更加清晰。在函数中,CACHE仅仅作为一个宏,与具体的实现无关。

  • 代码三:#undef CACHE

作用:撤销代码一中的定义。综合来讲,实现的功能就是:

定义了一个宏CACHE(x),把kmalloc_sizes.h文件里面的内容原封不动地复制到数组中,用完之后取消宏。数组展开之后便成:

struct cache_sizes malloc_sizes[] = {
    { .cs_size = (32) },
    { .cs_size = (64) },
    { .cs_size = (96) },
        ...
    { .cs_size = (ULONG_MAX) }
};

在linux/kmalloc_sizes.h中可以看到,所有的代码做的事,仅仅是针对不同的硬件环境作出不同的反应。除了默认的分配CHACH(x)运算外,针对内存页面、一级缓存大小,和是否支持内存管理模块,是否允许kmalloc分配大于1MB的内存等,分别作出了不同的动作。

在面向对象的编程中,最基本的三个概念为:继承、封装、抽象。继承可以理解为在在当前已经存在的基础上进行革新;封装也就是说将一些内部的东西不给你看,你只需要知道如何操作;抽象的意思就是程序有能力忽略正在处理中信息的某些方面,即对信息主要方面关注的能力。oop对于类的编程一般是这个流程:首先是对当前对象进行初始化,接着针对对象进行一系列的操作,最后对当前对象进行销毁。

再回到文首的三个代码运行流程,代码一首先定义一个宏;代码二接收到这个值,并且进行一系列的操作,是内部的,在slab.c中没有描述;代码三,销毁代码一定义的宏。同时,在mm/slab.c和include/linux/slab_def.h中可以多次看到上述三个代码,只是在每个代码的实现中,CACHE(x)定义为不同的宏。

用于kmalloc可分配的内存大小范围在32~131027(128k)字节,并且由于它用slab分配器(LINUX内存管理中SLAB分配器参见其他博文 https://blog.csdn.net/hs794502825/article/details/7981524 , https://blog.csdn.net/rockrockwu/article/details/79976833)来分配内存的,所以,得到的内存大小可能比你申请的要大一些(它向上取2的N次幂整数)。而且如果开启了CONFIG_LARGE_ALLOCS选项,这个值可以更大,可以达到了32M。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值