Linux内存管理 - slab分配器和kmalloc

本文目的在于分析Linux内存管理机制的slab分配器。内核版本为2.6.31。
1. SLAB分配器

内核须要常常分配内存,咱们在内核中最经常使用的分配内存的方式就是kmalloc了。前面讲过的伙伴系统只支持按页分配内存,但这个单位太大了,有时候咱们并不须要这么大的内存,好比我想申请128字节的空间,若是直接使用伙伴系统则需分配4KB的一整页,这显然是浪费。

slab分配器将页拆分为更小的单位来管理,来知足小于一页的内存需求。它将连续的几个页划分出更小的部分拿来分配相同类型的内存对象,对象的位置尽可能作到某种对齐要求(如处理器的L1高速缓存行对齐)以便不对CPU高速缓存和TLB带来显著影响。slab把一类对象统一管理,例如,划出两个页的内存,将其分红n小份用来分配一类对象的实例,同时slab也维护一些通用的对象,用来供kmalloc使用。

提供小块内存并非slab分配器的惟一任务,因为结构上的特色,它也用做一个缓存,主要针对常常分配内存并释放的对象。slab分配器将释放的内存保存在一个内部列表中,并不立刻返还给伙伴系统。在请求为该类对象分配一个实例时,会使用最近释放的内存块,这样就没必要触及伙伴系统以缩短分配消耗的时间,另外因为该内存块是“新”的,其驻留在CPU高速缓存的几率也会较高。

在下面的代码分析中,你会看到slab的这些“手段”是如何实现的。

先说一下“slab着色(slab coloring)”机制,是用来防止高速缓存冲突的:相同类型的slab、对象颇有可能被保存到相同的CPU cache的缓存行中,常用的对象被放到CPU cache中,这固然使咱们想要的,但若是两个不一样的对象每次都被放到相同的缓存行中,那交替的读取这两个对象,会致使缓存行的内容不断的被更新,也就没法体现缓存的好处了。不过个人内核版本是2.6.31,已经没有slab着色机制了,因此你们看到coloring什么的就不要再纠结了。

内核中还提供了slob和slub两种替代品,由于slab的结构很复杂,而且须要使用太多额外的空间去管理一类对象。关于这两个替代品我就很少说了,slub在性能和缓存占用方面都要优于slab,而且在一些嵌入式设备上会看到内核使用slub。
2. SLAB分配器的实现
2.1 SLAB分配器初始化

系统启动时slab分配器初始化的函数为kmem_cache_init()和kmem_cache_init_late()。函数名中的“cache”是指slab分配器,咱们也称做slab缓存,注意,它与CPU中的高速缓存没有关系,但上面讲到slab利用了高速缓存的特性。下面的分析中,我会使用“slab缓存”这种叫法。

kmem_cache_init()函数为分配slab对象准备最基本的环境。在分析这个函数以前,咱们先看一个内核中建立slab缓存的例子:

    static struct kmem_cache *nf_conntrack_cachep __read_mostly;
     
    nf_conntrack_cachep= kmem_cache_create("nf_conntrack",
                         sizeof(struct nf_conn),
                         0, SLAB_DESTROY_BY_RCU, NULL);

上面的代码在内核协议栈的连接跟踪模块中建立struct nf_conn的slab缓存,这个slab缓存用于分配struct nf_conn对象。

当想申请一个struct nf_conn结构的对象时,使用kmem_cache_alloc()函数进行分配。

    struct nf_conn *ct;
    ct =kmem_cache_alloc(nf_conntrack_cachep, gfp);

能够看到,建立和使用slab缓存是很是方便的。在/proc/slabinfo文件中能够看到内核中所建立的slab缓存。

kmem_cache_create()用于建立一个slab缓存,在哪里建立呢,kmem_cache_init()函数的工做就是初始化用于建立slab缓存的slab缓存。也就是说,一个slab缓存也应该是经过函数kmem_cache_create()来建立的,可是很容易想到,内核中的第一个slab缓存确定不能经过这个函数来建立,在内核中使用一个编译时生成的静态变量做为第一个slab缓存。

slab缓存用一个struct kmem_cache结构来描述。内核中的第一个slab缓存定义以下:

    static struct kmem_cache cache_cache = {
        .batchcount = 1,
        .limit = BOOT_CPUCACHE_ENTRIES,
        .shared = 1,
        .buffer_size = sizeof(struct kmem_cache),
        .name = "kmem_cache",
    };

系统中全部的slab缓存都被放入一个全局链表中:

staticstruct list_head cache_chain;

接下来咱们来分析kmem_cache_init()函数,它的实现分为下面几个步骤:

1.   建立cache_cache,它将用于分配系统中除了它自身之外的全部slab缓存的kmem_cache对象。

2.   建立能够分配struct arraycache_init和struct kmem_list3的slab cache。先建立这两个通用cache的缘由后面会讲到,他们也供kmalloc使用。这两个cache是经过kmem_cache_create()建立的,由于cache_cache已经可用了。这一步以后,将slab_early_init置为0。

3.   使用kmem_cache_create()建立剩余的通用cache,“剩余”是相对第2步中的两个cache,他们都是能够供kmalloc使用的。这些通用cache的名字和对象大小见下面表格。

4.   为cache_cache.array[]和malloc_sizes[INDEX_AC].cs_cachep->array[]从新分配空间。

5.   为cache_cache.nodelists[]、malloc_sizes[INDEX_AC].cs_cachep-> nodelists[]和malloc_sizes[INDEX_L3].cs_cachep-> nodelists[]从新分配空间。

通用cache的名字和对象大小用两个数组来记录:malloc_sizes[]和cache_names[]。

cache_names[]
    

malloc_sizes[]

size-32
    

32

size-64
    

64

size-96
    

96

size-128
    

128

……
    

……

NULL
    

ULONG_MAX

对于数据结构的其余细节先不作讨论,在讲到在一个cache上分配对象时会说明数据结构。

这部分须要注意一些静态变量,在初始化cache_cache的时候只用到了静态分配的变量,他们之间的关系以下图,其中全局变量用红色标出。


这时尚未用户本身建立的slab cache,因此这里看到的都是通用cache。这些通用cache主要供kmalloc使用。在这期间,不要使用kmalloc函数。
2.2 建立SLAB缓存

除了cache_cache自身,建立一个slab缓存的方法为kmem_cache_create()。slab缓存分为on-slab和off-slab两种,on-slab就是slab管理信息和它所管理的对象放在一块儿,off-slab就是slab管理信息和他所管理的对象分开存放,后面会看到为何会区分这两种类型的slab。

咱们先了解一下struct kmem_cache结构体的成员。

    struct kmem_cache {
    /* 1)per-cpu data, touched during every alloc/free */
        struct array_cache *array[NR_CPUS]; /*per-CPU缓存 */
    /* 2)Cache tunables. Protected by cache_chain_mutex */
        unsigned int batchcount;
        unsigned int limit;
        unsigned int shared;
     
       /*每次分配的大小,如nf_conn的cache的buffer_size为sizeof(structnf_conn) */
        unsigned int buffer_size;
        u32 reciprocal_buffer_size;
    /* 3)touched by every alloc & free from the backend */
        unsigned int flags;      /* constant flags */
        unsigned int num;    /* # of objs per slab */
     
    /* 4)cache_grow/shrink */
        /* order of pgs per slab (2^n) */
        unsigned int gfporder;
     
        /* force GFP flags, e.g. GFP_DMA */
        gfp_t gfpflags;
     
        size_t colour;           /* cache colouring range */
        unsigned int colour_off; /* colour offset */
        /* 为slab管理信息分配空间的cache。 */
        struct kmem_cache *slabp_cache;
        /* slab管理信息的size,即struct slab和n个kmem_bufctl_t */
        unsigned int slab_size;
        unsigned int dflags;     /* dynamic flags */
     
        /* constructor func */
        void (*ctor)(void *obj);
     
    /* 5) cache creation/removal */
        const char *name;
        struct list_head next;
     
        /*
        * We put nodelists[] at the end ofkmem_cache.
        * We still use [MAX_NUMNODES] and not [1] or[0] because cache_cache
        * is statically defined, so we reserve themax number of nodes.
        */
        struct kmem_list3 *nodelists[MAX_NUMNODES];
        /*
         * Donot add fields after nodelists[]
         */
    };

kmem_cache_create()函数的声明以下:

    struct kmem_cache *
    kmem_cache_create(const char *name, size_t size, size_t align,
        unsigned long flags, void (*ctor)(void *));

五个参数分别为:

name:要建立的cache的名字,将赋值给kmem_cache结构的name成员。

size:要建立的cache每次分配对象的大小,将赋值给kmem_cache结构的buffer_size成员。

align:分配对象以及slab管理信息的对齐量,基本上都为0,即便用默认的对齐方式。

flags:标记,kmem_cache结构的flags成员。

ctor:分配新的slab的时候的构造函数,kmem_cache结构的ctor成员。

这个函数的工做以下:

1.   根据flags、CPU的cache line以及传入的align参数,调整slab管理信息的的对齐量。

2.   用kmem_cache_zalloc(&cache_cache, gfp)在cache_cache上分配一个kmem_cache结构实例cachep。

3.   若是对象的size不小于(PAGE_SIZE >> 3),而且全局标记slab_early_init=0,就强制给flags设置CFLGS_OFF_SLAB。

4.   根据align调整buffer_size大小,并调用calculate_slab_order()函数,该函数从order=0寻找最小的order知足2^order个页的大小可用于分配至少一个对象,找到以后给cachep->num和cachep->gfporder赋值,num成员为2^gfporder个页可分配多少个对象,函数返回值left_over为剩余的空间。对于on slab的cache而言,知足cachep->num * cachep->buffer_size+ cachep->slab_size + left_over = (2 ^ cachep->gfporder) * PAGE_SIZE。而对于off slab的cache而言,知足cachep->num * cachep->buffer_size + left_over = (2 ^ cachep->gfporder)* PAGE_SIZE,即没有slab管理信息的空间,由于off slab的cache的管理信息单独放到另外一个地方。

5.   若是left_over比slab管理信息空间大,且cachep是off slab的,则把cachep改成on slab的,即清除CFLGS_OFF_SLAB标记。同时将left_over的值减掉slab管理信息的大小。

6.   给cachep的一些成员赋值:

        cachep->colour_off = cache_line_size();
        /* Offset must be a multiple of thealignment. */
        if (cachep->colour_off < align)
           cachep->colour_off = align;
        cachep->colour = left_over /cachep->colour_off;
        cachep->slab_size = slab_size;
        cachep->flags = flags;
        cachep->gfpflags = 0;
        if (CONFIG_ZONE_DMA_FLAG && (flags& SLAB_CACHE_DMA))
           cachep->gfpflags |= GFP_DMA;
        cachep->buffer_size = size;
        cachep->reciprocal_buffer_size =reciprocal_value(size);

7.   若是cachep是off slab的, slab管理信息单独放在其余一个地方。这个地方就是根据slab_size(slab管理信息的大小)在通用cache上选择一个合适的cache,注意这里只是选择cache,没有给slab信息分配空间。选好的cache赋值给cachep->slabp_cache。

8.   对cachep->nodelists[0]和cachep->array[0]赋值。调用的函数为setup_cpu_cache(),这个函数中根据全局变量g_cpucache_up的值给cachep的两个成员分配不一样的值。最终结果就是为cachep->array[0]分配sizeof(void *) * cachep->batchcount+ sizeof(struct array_cache)大小的空间,其中cachep->batchcount是struct array_cache中entry的数目。cachep->nodelists[0]中的三个链表都初始为空。

9.   将cachep加入到全局链表:list_add(&cachep->next, &cache_chain);

g_cpucache_up变量:

这个变量记录了在不一样阶段,slab缓存初始化的状态,它可取的值有:

    static enum {
       NONE,
       PARTIAL_AC,
       PARTIAL_L3,
       EARLY,
       FULL
    } g_cpucache_up;

在kmem_cache_init()和kmem_cache_init_late()之间没有调用过kmem_cache_create(),即在g_cpucache_up等于EARLY和FULL之间没有建立过cache。也就是说,g_cpucache_up除了FULL以外的全部状态都只在kmem_cache_init()中有用到,即只有建立通用cache有用到。

假定INDEX_AC=0,PARTIAL_L3=3。下表显示了随着建立不一样的slab缓存,g_cpucache_up记录的状态的变化:

cache名称
    

g_cpucache_up

cache_cache
    

NONE

size-32(array cache)
    

NONE

size-64(kmem_list3 structures)
    

PARTIAL_AC

其余通用cache
    

PARTIAL_L3

自定义cache
    

FULL

 
2.3 在SLAB缓存分配空间

在slab缓存中分配对象,使用的函数是kmem_cache_alloc()或kmem_cache_zalloc(),函数返回void *类型指针。实际的分配工做由____cache_alloc()完成,它的函数体很简单:

    static inline void *____cache_alloc(struct kmem_cache *cachep, gfp_t flags)
    {
        void *objp;
        struct array_cache *ac;
     
        ac = cpu_cache_get(cachep); /* ac =cachep->array[0] */
        if (likely(ac->avail)) {
            ac->touched= 1;
           objp = ac->entry[--ac->avail]; /* 最后一个entry */
        } else {
           objp = cache_alloc_refill(cachep, flags);
        }
        return objp;
    }

若是cachep->array[0]->avail不为0,则直接从cachep->array[0]->entry[]末尾取一个对象返回,并将avail的值减1。

若是cachep->array[0]->avail为0,即没有可用对象可分配,则调用cache_alloc_refill()。

struct kmem_cache的array数组的每一个元素都是一个per-CPU缓存,slab分配一个对象,最终都是先填充到这个缓存中,再在这上面分配出去的。

    struct kmem_cache {
        /* 1) per-cpu data, touched during everyalloc/free */
        struct array_cache *array[NR_CPUS]; /*per-CPU缓存 */
        ……
    }

struct array_cache结构体定义以下:

    struct array_cache {
       unsigned int avail; /* 该缓存中可用对象的数目 */
       unsigned int limit; /* 对象数目的限制,在释放缓存时使用 */
       /* 若是该缓存中没有对象可分配了,每次须要向slab申请填充对象的数量 */
       unsigned int batchcount;
       unsigned int touched; /* 该缓存是不是活动的(最近分配过对象) */
       spinlock_t lock;
       void *entry[]; /* 实际的对象存放在这儿 */ /*
               * Must have thisdefinition in here for the proper
               * alignment ofarray_cache. Also simplifies accessing
               * the entries.
               */
    };

在per-CPU缓存上分配对象时,是从后往前分配的,每分配出一个对象,avail减1,因此avail除了表示可用的对象数量,仍是一个数组下标,能够经过array.entry[avail]直接获取对象。而在释放对象时,则先放到per-CPU缓存的最后,由于内核假设刚释放的对象仍然处在CPU高速缓存中,会尽快在此分配它。

per-CPU缓存的entry[]是一个指针数组,因此它只是存放对象的指针,真正的对象在slab缓存中。

咱们先看一下一个on slab的cache中每一个slab的结构:


图中,slab中的对象总数由cachep->num记录。colour为着色区,咱们不去关注。slab管理信息部分包括一个struct slab结构,以及cachep->num个kmem_bufctl_t的值,方便来定位某个对象。浅蓝色的对象区域存放实际的对象,每一个对象大小由cachep->buffer_size指出。能够看出,一个slab缓存中,有不少空间用做了管理信息。

struct slab结构体定义以下,注意inuse和free两个成员的含义:

    struct slab {
       struct list_head list;
       unsigned long colouroff;
       void *s_mem;
       /* 已经被使用的对象,最多为cachep->num*/
       unsigned int inuse;
    /* 标识当前还未被分配的对象的kmem_bufctl_t区域偏移量 */
       kmem_bufctl_t free;
       unsigned short nodeid;
    };

咱们使用kmem_cache_alloc(cachep, flags)在cache上申请一个对象时的分配步骤以下:

1.   先试图在array cache即cachep->array[0]->entry[]上获取,它上面可分配的对象的数目由cachep->array[0]->avail记录。若是这一步找到了可用对象,就返回ac->entry[--ac->avail]。注意,在kmem_cache_create的时候,没有分配对象的空间,因此avail确定是0的。

2.   当avail=0的时候,则array cache已经用完。就试图在kmem_list3即cachep->nodelists[0]上面分配,这就是cache_alloc_refill()函数须要作的事情。structkmem_list3中有三个双向链表,分别指向“已部分使用的slab”、“已用尽的slab”和“彻底空闲的slab”。


1) cache_alloc_refill()先查找slabs_partial链表中有没有slab,若是有则在这里分配;若是没有则查找slabs_free链表中有没有slab,若是有则在这里分配。在这两个链表上分配对象的方式为:将slab中的cachep->array[0]->batchcount数量的对象“分配”给cachep->array[0]->entry[],这个“分配”的过程只是指针指向的操做。而后cachep->array[0]的inuse成员的值增长,free成员前移,同时更新cachep->array[0]->avail的值。这样,array cache上又可分配对象了,因而返回ac->entry[--ac->avail]。

注意,在slab分配出去batchcount个对象后,须要判断该slabs链表是否用尽,若是用尽就将其转移到slabs_full链表中。

2) 若是在slabs_partial和slabs_free链表中都没有slab对象了,就须要在内存中从新分配一个slab。这是cache_grow()函数的工做。

3.   cache_grow()函数的主要任务就是在伙伴系统中分配2^(cachep->gfporder)个页,并给分配的页都加上PG_slab标记使其为slab使用。该函数具体实现以下:

a)     算出这次分配的slab的colour空间,其实这些结构体的colour相关的成员在内核中并无用到。下面代码中cachep->colour是在建立cache的时候经过left_over计算的,为颜色数量,l3->colour_next是当前选择cachep->colour中的哪一个颜色,cachep->colour_off是每一个颜色占用的空间大小。最后算出的offset便是上图中最开始的colour区域的长度。

        l3 = cachep->nodelists[nodeid];
        spin_lock(&l3->list_lock);
        offset = l3->colour_next;/* init as 0 */
        l3->colour_next++;
        if (l3->colour_next >= cachep->colour)
           l3->colour_next = 0; /* 取值为0 ~ colour-1 */
        spin_unlock(&l3->list_lock);
        offset *= cachep->colour_off;

例如若是一个cache的cachep->colour=3,即有三种颜色,则分配的slab开头的colour区域的长度就可能为0、cachep->colour_off和2 * cachep->colour_off。

b)    分配2^(cachep->gfporder)个页,并给分配的页加上PG_slab标记。分配成功后得到第一个页的起始地址的指针objp。

c)     在objp中分配slab管理信息的空间。这里分为两种状况,若是cache是on slab的,则直接在objp的地址上非slab管理信息分配空间,并给struct slab的成员赋值。而若是是off slab的,即slab管理信息和slab对象不在一块儿,这时slab管理信息对象是在cachep->slabp_cache上分配的,分配的函数依然是调用kmem_cache_alloc()。

d)    将分配的全部页与所属的slab和cache创建映射关系,具体作法为,将objp开始的2^(cache->gfporder)个页对应的struct page都进行如下赋值:

    page->lru.next = (structlist_head *)cache;
    page->lru.prev = (structlist_head *)slab;

其中cache和slab是当前的cache和刚分配的slab信息。这样的目的是为了能够方便的找到一个对象所属于的slab和cache。

e)    调用每一个对象的ctor方法,并给每一个对象对应的kmem_bufctl_t赋个值,这个值从1开始,直到cachep->num-1,最后一个kmem_bufctl_t赋值为BUFCTL_END。

f)     将分配好的slab添加到l3的slabs_free列表中,即cachep->nodelists[0]->slabs_free列表。同时l3->free_objects += cachep->num,注意这个值是l3中的三个列表中可用对象的总数,但不是cachep中可用对象的总数,由于ac->entry[]中还有。

cache_grow()返回后,从新执行cache_alloc_refill()函数,这时即可以在上述的步骤中即可以找到一个对象来返回。

咱们再回过头来看一下为cachep分配slab管理信息的函数alloc_slabmgmt()。上面的c)步骤中讲到在off slab的时候要在cachep->slabp_cache上分配slab管理信息,咱们知道这个slabp_cache是在kmem_cache_create的时候根据slab_size大小在通用cache上选择的一个合适的cache。而在分配slab管理信息的时候,slab管理信息做为cache的对象slabp_cache又会有它本身的slab管理信息,这样又会重复这一分配动做,一定会致使递归,固然递归的前提是slabp_cache是off slab的,也就说,slabp_cache不能是off slab的。我实际看到的slabp_cache都是size-64或size-32,所以都是on slab的,我人为的将size-64或size-32改成off slab的,不出所料,kernel就起不来了。

若是cache是off slab的,那它的slab结构分为两部分:


2.4 在SLAB缓存释放空间

释放slab对象使用kmem_cache_free()函数,它直接调用了__cache_free()函数。

释放一个对象时,分为两种状况:

1. 若是per-CPU缓存中可用对象数目小于其limit的限制,则直接将对象释放到per-CPU缓存中。

2. 若是per-CPU缓存中可用对象数目达到其limit的限制,则须要先将batchcount数目的对象释放到slab缓存中,这个释放动做顺序为从前日后(即释放下标为0~batchcount-1的对象),由于这时最开始释放的对象极可能已经不在高速缓存中了。而后再将咱们要释放的对象释放到per-CPU缓存中,而且将以前下标为batchcount以及以后的对象前移。

    static inline void__cache_free(struct kmem_cache *cachep, void *objp)
    {
       struct array_cache *ac = cpu_cache_get(cachep);
     
       ……
     
       if (likely(ac->avail < ac->limit)) {
           ac->entry[ac->avail++] = objp;
           return;
       } else {
           cache_flusharray(cachep, ac);
           ac->entry[ac->avail++] = objp;
       }
    }

释放部分对象到slab缓存中的函数为cache_flusharray(),最终经过free_block()完成的,free_block()函数的工做是:

1. 获取对象所在的slab缓存,这是经过virt_to_page()来完成的。前面在分析cache_grow()函数时讲到过slab和page的关系。

2. 将获得的slab从缓存链表中删除。

3. 将对象放回到slab中。

4. 将slab从新添加到缓存链表中,分两种状况:若是这时slab中全部对象都是未使用的,就将其放到slabs_free链表中,不然将其放到slabs_partial链表中。另外,若是将slab放到slabs_free链表,会先检查缓存中空闲对象数目总数是否超过了预约义的free_limit限制,若是超过了,则直接调用slab_destroy()释放掉这个slab。

    static voidfree_block(struct kmem_cache *cachep, void **objpp, int nr_objects,
                  int node)
    {
       int i;
       struct kmem_list3 *l3;
     
       for (i = 0; i < nr_objects; i++) {
           void *objp = objpp[i];
           struct slab *slabp;
          
           /* 获取对象所在的slab */
           slabp = virt_to_slab(objp);
           l3 = cachep->nodelists[node];
     
           /* 将slab删除 */
           list_del(&slabp->list);
           check_spinlock_acquired_node(cachep, node);
           check_slabp(cachep, slabp);
     
           /* 将对象放回slab中 */
           slab_put_obj(cachep, slabp, objp, node);
           STATS_DEC_ACTIVE(cachep);
           l3->free_objects++;
           check_slabp(cachep, slabp);
     
           /* 将slab从新添加到缓存链表中 */
           if (slabp->inuse == 0) {
              /* 若是对象总数超出限制,释放整个slab */
              if (l3->free_objects > l3->free_limit) {
                  l3->free_objects -= cachep->num;
                  /* No need to drop any previously held
                   * lock here,even if we have a off-slab slab
                   * descriptor itis guaranteed to come from
                   * a differentcache, refer to comments before
                   *alloc_slabmgmt.
                   */
                  slab_destroy(cachep, slabp);
              } else {
                  /* 添加到slabs_free链表中的开头 */
                  list_add(&slabp->list, &l3->slabs_free);
              }
           } else {
              /* Unconditionally move a slab to the end of the
               * partial list onfree - maximum time for the
               * other objects tobe freed, too.
               */
              /* 添加到slabs_partial链表的末尾 */
              list_add_tail(&slabp->list,&l3->slabs_partial);
           }
       }
    }

2.5 销毁SLAB缓存

要销毁一个slab缓存(struct kmem_cache结构的实例),须要调用kmem_cache_destroy()函数,该函数删除缓存的步骤为:

1. 将cachep从cache_cache链表中删除。

2. 将cachep中全部对象释放掉,空间还给伙伴系统。若是该slab缓存是off-slab的,还要将slab管理信息从cachep->slabp_cache中释放。

3. 将cachep的per-CPU缓存和struct kmem_list3结构释放。

4. 因为cachep是cache_cache的一个对象,因此须要将cache_cache中将该对象删除,这一步使用了kmem_cache_free()函数,将slab缓存包括它管理的全部对象都释放。
2. kmalloc

slab是kmalloc的基础,kmalloc使用上面讲到的通用slab缓存来分配空间。

void *kmalloc(size_t size,gfp_t flags);

kmalloc可分配的最大size由KMALLOC_MAX_SIZE定义,这个值在2^25B和buddy的最大分配阶之间取一个小值。

    #defineKMALLOC_SHIFT_HIGH  ((MAX_ORDER +PAGE_SHIFT - 1) <= 25 ? \
                  (MAX_ORDER + PAGE_SHIFT - 1) : 25)
    #define KMALLOC_MAX_SIZE (1UL<< KMALLOC_SHIFT_HIGH)
    #defineKMALLOC_MAX_ORDER   (KMALLOC_SHIFT_HIGH -PAGE_SHIFT)

kmalloc的实现很简单:

1.   根据size大小找到最小能装下一个对象的通用cache。

2.   调用kmem_cache_alloc(cachep,flags)进行分配。

由此可知,kmalloc分配的空间是物理上连续的。

原文:https://blog.csdn.net/jasonchen_gbd/article/details/44024009 node

相关文章
相关标签/搜索