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

Linux内存管理 - slab分配器和kmalloc的更多相关文章

  1. linux内存管理--slab及其代码解析

    Linux内核使用了源自于 Solaris 的一种方法,但是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存作为对象按照大小进行分配,被称为slab高速缓存. 内存管理的目标是提供一种方法,为实 ...

  2. 【原创】(十一)Linux内存管理slub分配器

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  3. 内存管理-slab[代码]

    主要介绍kmalloc和kfree代码流程,侧重kmalloc和kfree流程中锁使用规则,会引用到cpuset,mempolicy(内存策略),numa相关知识.如果读起来比较困难可以参考另一篇随笔 ...

  4. Linux内存管理 (5)slab分配器

    专题:Linux内存管理专题 关键词:slab/slub/slob.slab描述符.kmalloc.本地/共享对象缓冲池.slabs_partial/slabs_full/slabs_free.ava ...

  5. Linux内存管理之slab分配器

    slab分配器是什么? 参考:http://blog.csdn.net/vanbreaker/article/details/7664296 slab分配器是Linux内存管理中非常重要和复杂的一部分 ...

  6. linux内存管理之malloc、vmalloc、kmalloc的区别

    kmalloc kzalloc vmalloc malloc 和get_free_page()的区别 一.简述 1. kmalloc申请的是较小的连续的物理内存,虚拟地址上也是连续的.kmalloc和 ...

  7. Linux内存管理原理

    本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址又叫线性地址.linux没有采用分段机制,所以逻辑地址和虚拟地址(线性地址)(在用户态,内核态逻 ...

  8. Linux内存管理原理【转】

    转自:http://www.cnblogs.com/zhaoyl/p/3695517.html 本文以32位机器为准,串讲一些内存管理的知识点. 1. 虚拟地址.物理地址.逻辑地址.线性地址 虚拟地址 ...

  9. Windows内存管理和linux内存管理

    windows内存管理 windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或 ...

随机推荐

  1. c++ 封装线程库 0

    1.互斥锁简介 互斥锁主要用于互斥,互斥是一种竞争关系,用来保护临界资源一次只被一个线程访问. POSIX Pthread提供下面函数用来操作互斥锁. int pthread_mutex_init(p ...

  2. python17 多线程学习

    多线程 多任务可以由多进程完成,也可以由一个进程内的多线程完成. 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程. 由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的 ...

  3. JavaSE---多线程---线程的生命周期

    1.线程的生命周期:新建.就绪.运行.阻塞.死亡 2.运行状态线程进入阻塞: 1.1 调用sleep方法主动放弃: 1.2 调用线程的suspend方法将线程挂起,不推荐使用: 1.3 线程调用一个阻 ...

  4. Selenium + Python操作IE 速度很慢的解决办法

    IEDriverServer 64位换成32位 https://docs.seleniumhq.org/download/

  5. 3d Max 2016安装失败怎样卸载3dsmax?错误提示某些产品无法安装

    安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).AUTODESK系列软件着实令人头疼,有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...

  6. Python 的命名空间

    Python命名空间的本质: 一.命名空间的定义: 二.命名空间的查找顺序: 三.命名空间的生命周期: 四.通过locals()和globals() BIF访问命名空间. 重点是第四部分,我们将在此部 ...

  7. Hadoop学习笔记(3) Hadoop I/O

    1. HDFS的数据完整性 HDFS会对写入的所有数据计算校验和,并在读取数据时验证校验和.datanode负责在验证收到的数据后存储数据及其校验和.正在写数据的客户端将数据及其校验和发送到由一系列d ...

  8. 微信小程序电商实战-商品详情(上)

    先看一下今天要实现的小程序商品详情页吧!   商品详情.gif 本期我们要实现小程序商品详情页的头部标题.头部轮播.商品详情浮动按钮和商品内页布局. 一.设置头部标题 如上图所示,头部标题是商品详情 ...

  9. The thirteen day

    Well begun is hanlf done 良好的开端是成功的一半.(此句是省略句,Something that is well begun is something that is half ...

  10. 【起航计划 028】2015 起航计划 Android APIDemo的魔鬼步伐 27 App->Preferences->Launching preferences 其他activity获取Preference中的值

    前给例子介绍了如何使用PreferenceActivity 来显示修改应用偏好,用户对Preferences的修改自动存储在应用对应的Shared Preferences中. 本例介绍了如何从一个Ac ...