首页 技术 正文
技术 2022年11月11日
0 收藏 926 点赞 2,560 浏览 15579 个字

本文目的在于分析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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:8,991
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,505
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,349
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,134
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,766
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,844