linux 源代码分析6:Slab分配器

  1. Slab分配器
    1. struct slab 结构:
    2. structarray_cache 结构:
    3. structkmem_list3 结构:
    4. structkmem_cache 结构:

Slab分配器

伙伴系统一次内存分配最少都是一页,实际上很多时候我们分配内存的时候都只是一小块,不需要一页这样大的空间。
这时候我们可以从slab来分配内存,Slab是伙伴系统之上的一个内存分配器,设计slab的目的是避免伙伴系统的一些缺陷。
内存分配的实质就是根据申请者申请的参数查找一块合适的内存,然后把内存地址返回。伙伴系统是最底层的内存管理系统,Slab是在伙伴系统之上的一个内存分配器,slab把从伙伴系统申请的内存块分割成更小的块来管理,分配的时候查找一个合适的块返回首地址。
slab从伙伴系统申请的内存块用struct slab描述,但Slab并不是直接基于struct slab来管理内存,因为还有几个需要考虑的因素:
一:在多cpu环境下,访问一个变量需要获得回环锁,如果对每个cpu缓存一些内存空间,这样可以提高性能。Slab采取的方法是对每个cpu缓存一些对象,如果cpu缓存的对象为空了则一次从获取一批对象缓存起来。为了这个因素,定义了结构struct array_cache。
二:在numa系统上,每个节点上的访问速度可能不一样,分配时候需要选择合适节点来进行分配,对slab块也应该能按节点管理。针对这个因素定义了结构structkmem_list3。
三:能同时对多个slab块进行管理,可以提高程序灵活性。在structkmem_list3中设定了三个链表分别是块,闲,和半空slab块链表。
处于slab顶层的是slab缓存,slab缓存用struct kmem_cache描述。上面的结构都在struct kmem_cache中组织起来。每次内存分配都是在一个slab缓存中进行的,一个slab缓存中分配的对象的长度都是相同的。
下面是这些结构的定义:

struct slab 结构:

struct slab {
    union {
        struct {
            struct list_headlist;    //一个缓存中的所有slab块组成的链表
            unsigned longcolouroff;      //着色区的大小
            void *s_mem;      //指向对象区的起点
            unsigned int inuse;  //Slab中所分配对象的个数
            kmem_bufctl_t free;        //指向空闲对象链中的第一个对象
            unsigned shortnodeid;    //slab块所在结点的结点号
        };
    struct slab_rcu__slab_cover_slab_rcu;
    };
};

structarray_cache 结构:

structarray_cache {
    unsigned int avail;        //当前缓存的对象数目
    unsigned int limit;        //最多可以缓存的对象数目
    unsigned int batchcount;    //缓存对象空的情况下一次获取的对象数
    unsigned int touched;      //用来表示这个对象最新是否被使用
    spinlock_t lock;    //回环锁
    void *entry[];       //指向缓存对象数组
};

structkmem_list3 结构:

structkmem_list3 {
    struct list_head slabs_partial;        //分配空的slab块链表
    struct list_head slabs_full;           //没有空闲对应的slab块链表
    struct list_head slabs_free;         //空闲的slab块链表
    unsigned long free_objects;    //空闲对象数
    unsigned int free_limit;       //空闲对象数上限
    unsigned int colour_next;        //下一个颜色
    spinlock_t list_lock;
    struct array_cache *shared;   //共享的对象缓存
    struct array_cache **alien;    /* on other nodes */
    unsigned long next_reap;        //定义了内核在两次尝试收缩缓存之间,必须经过的时间间隔
    int free_touched;         //表示三链表是否是活动的
};

structkmem_cache 结构:

structkmem_cache {
    /* 1) Cachetunables. Protected by cache_chain_mutex */
    unsigned int batchcount;    //一批对象的数量
    unsigned int limit;        //空闲对象数上限
    unsigned int shared;        //共享对象数
    unsigned int buffer_size;    //对象实际长度
    u32 reciprocal_buffer_size;     //
    /* 2) touched byevery alloc & free from the backend */
    unsigned int flags;       //一些固定的标志位
    unsigned int num;        //每个slab块包含的对象数
    /* 3)cache_grow/shrink */
    /* order of pgs per slab (2^n) */
    unsigned int gfporder;     //slab块的阶
    /* force GFP flags, e.g. GFP_DMA */
    gfp_t gfpflags;     //分配标志位
    size_t colour;      //颜色数
    unsigned int colour_off;      //色差,相邻两种颜色的便宜值的差
    struct kmem_cache *slabp_cache;    //如果slab块的管理数据是独立的,则在这个缓存中分配空间
    unsigned int slab_size;            
    unsigned int dflags;     /* dynamic flags */
    /* constructor func */
    void (*ctor)(void *obj);         //构造函数
    /* 4) cachecreation/removal */
    const char *name;       //缓存名称,在整个系统中是唯一的
    struct list_head next;      //所有的slab缓存用这个结果连接成一个链表
    /* 5) statistics*/
    #ifdefCONFIG_DEBUG_SLAB
        unsigned long num_active;
        unsigned long num_allocations;
        unsigned long high_mark;
        unsigned long grown;
        unsigned long reaped;
        unsigned long errors;
        unsigned long max_freeable;
        unsigned long node_allocs;
        unsigned long node_frees;
        unsigned long node_overflow;
        atomic_t allochit;
        atomic_t allocmiss;
        atomic_t freehit;
        atomic_t freemiss;
        /*
         * If debugging is enabled, then the allocatorcan add additional
         * fields and/or padding to every object.buffer_size contains the total
         * object size including these internal fields,the following two
         * variables contain the offset to the userobject and its size.
         */
        int obj_offset;
        int obj_size;
    #endif /*CONFIG_DEBUG_SLAB */
    /* 6)per-cpu/per-node data, touched during every alloc/free */
    /*
     * We put array[] at the end of kmem_cache,because we want to size
     * this array to nr_cpu_ids slots instead ofNR_CPUS
     * (see kmem_cache_init())
     * We still use [NR_CPUS] and not [1] or [0]because cache_cache
     * is statically defined, so we reserve the maxnumber of cpus.
     */
    struct kmem_list3 **nodelists;           //指向三链表数组
    struct array_cache *array[NR_CPUS];    //对每个cpu,指向每个cpu的对象缓存
    /*
     * Do not add fields after array[]
     */
};

对象:一块内存空间,用空间的首地址标识。
三链表:对应struct kmem_list3,包含空,满,部分空三个链表。
对象缓存;缓存堆栈:对应struct array_cache,用来缓存一些对象。
缓存;slab缓存:对应struct kmem_cache。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 ancjf@163.com

文章标题:linux 源代码分析6:Slab分配器

本文作者:ancjf

发布时间:2013-06-03, 19:28:59

最后更新:2020-04-18, 15:57:43

原始链接:http://ancjf.com/2013/06/03/linux-3-4-10-%E5%86%85%E6%A0%B8%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%BA%90%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%906-Slab%E5%88%86%E9%85%8D%E5%99%A8/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏