Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3759464
  • 博文数量: 880
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 6155
  • 用 户 组: 普通用户
  • 注册时间: 2016-11-11 09:12
个人简介

To be a better coder

文章分类

全部博文(880)

文章存档

2022年(5)

2021年(60)

2020年(175)

2019年(207)

2018年(210)

2017年(142)

2016年(81)

分类: LINUX

2016-12-21 17:13:58

Linux采用了slab来管理小块内存的分配与释放,Slab的提出基于以下两个考虑:

1. 内核函数经常倾向于反复请求相同的数据类型

2. 不同的结构使用不同的分配方法可以提高效率

3. 伙伴系统的频繁申请/释放影响效率,将释放内存放入缓冲区,直至超过阀值再归还给伙伴系统

4. 可以缓解内存碎片的产生,不过产生了内存浪费

 

Slab分为专用高速缓存(用于存放专用数据结构,skb/mm等)和普通高速缓存(普通的kmalloc之类)。所有高

速缓存区通过链表组织在一起,首节点是cache_chain。普通高速缓存分为(2^0)(2^1)(2^2)......,区域的个数

以及大小与系统内存配置以及PAGE_SIZE/L1_CACHE_BYTES/KMALLOC_MAX_SIZE等相关,具体在

linux/kmalloc_sizes.h中定义,每个大小对应两个高速缓存,一个是DMA高速缓存,一个是常规高速缓存。他们

存放在struct cache_sizes malloc_sizes[]中。

 

Slab把每一个请求的内存称之为对象,并将对象存放在slab中,具体的slab按照空、未满和全满放在不同的链表

中,全部连接至高速缓存中。如下


Slab分配器的相关数据结构:

点击(此处)折叠或打开

  1. static DEFINE_MUTEX(cache_chain_mutex);    // 保护相关的链表操作
  2. static struct list_head cache_chain;       // 串联各kemem_cache

点击(此处)折叠或打开

  1. /*
  2.  * struct array_cache
  3.  *
  4.  * Purpose:
  5.  * - LIFO ordering, to hand out cache-warm objects from _alloc
  6.  * - reduce the number of linked list operations
  7.  * - reduce spinlock operations
  8.  *
  9.  * The limit is stored in the per-cpu structure to reduce the data cache
  10.  * footprint.
  11.  *
  12.  */
  13. struct array_cache {
  14.     unsigned int avail;        // 当前空闲对象的位置,取值时objp = ac->entry[--ac->avail]
  15.     unsigned int limit;        // 空闲对象上限
  16.     unsigned int batchcount;   // 每次需要申请对象数量或释放的对象数量,和kmem_cache相同
  17.     unsigned int touched;      // 如果从该组中分配了对象,就设置为1
  18.     spinlock_t lock;           
  19.     void *entry[];    /*       // 指向可用的slab对象指针数组
  20.              * Must have this definition in here for the proper
  21.              * alignment of array_cache. Also simplifies accessing
  22.              * the entries.
  23.              */
  24. };

点击(此处)折叠或打开

  1. /*
  2.  * The slab lists for all objects.
  3.  */
  4. struct kmem_list3 {
  5.     struct list_head slabs_partial;      /* 部分已分配的链表    partial list first, better asm code */
  6.     struct list_head slabs_full;         /× 已经全部分配的链表 ×/
  7.     struct list_head slabs_free;         /× 尚未分配的链表 ×/
  8.     unsigned long free_objects;          /× partial和free上可用的对象个数 ×/
  9.     unsigned int free_limit;             /× 所有slab上允许未使用的对象最大数目 ×/
  10.     unsigned int colour_next;            /* 下一个slab的颜色 Per-node cache coloring */
  11.     spinlock_t list_lock;
  12.     struct array_cache *shared;          /* 节点内共享 shared per node */
  13.     struct array_cache **alien;          /* 在其他节点上 on other nodes */
  14.     unsigned long next_reap;             /* 尝试两次缓存回收必须经过的时间间隔 updated without locking */
  15.     int free_touched;                    /* 缓存是否活动的,0回收,1申请对象 updated without locking */
  16. };

点击(此处)折叠或打开

  1. /*
  2.  * struct slab
  3.  *
  4.  * Manages the objs in a slab. Placed either at the beginning of mem allocated
  5.  * for a slab, or allocated from an general cache.
  6.  * Slabs are chained into three list: fully used, partial, fully free slabs.
  7.  */
  8. struct slab {
  9.     struct list_head list;            /× Slab所在的链表 ×/
  10.     unsigned long colouroff;          /× Slab中第一个对象的偏移 ×/
  11.     void *s_mem;                      /* 第一个对象的地址 including colour offset */
  12.     unsigned int inuse;               /* 被使用的对象的数目 num of objs active in slab */
  13.     kmem_bufctl_t free;               /× 下一个空闲对象的下标 ×/
  14.     unsigned short nodeid;            /× NUMA ID,节点标识号 ×/
  15. };

点击(此处)折叠或打开

  1. /*
  2.  * struct kmem_cache
  3.  *
  4.  * manages a cache.
  5.  */

  6. struct kmem_cache {
  7. /* 1) per-cpu data, touched during every alloc/free */
  8.     struct array_cache *array[NR_CPUS];            /× Per_cpu结构,用于节点的申请与释放 ×/
  9. /* 2) Cache tunables. Protected by cache_chain_mutex */
  10.     unsigned int batchcount;                       /× Array中没有空闲对象时,每次为数组分配的对象数目 ×/
  11.     unsigned int limit;                            /× Array中允许的最大对象数目,超出后会将batchcount个对象返回给slab ×/
  12.     unsigned int shared;                           /× 是否共享CPU高速缓存 ×/

  13.     unsigned int buffer_size;                      /* Slab中对象的大小,对象长度+填充字节 */
  14.     u32 reciprocal_buffer_size;                    /× slab中对象大小的倒数,加快计算 ×/
  15. /* 3) touched by every alloc & free from the backend */

  16.     unsigned int flags;                            /* 高速缓存永久化的标志,当前只有CFLGS_OFF_SLAB,用于标识slab管理数据是在slab内还是外 */
  17.     unsigned int num;                              /* 封装在一个单独slab中的对象的数目 # of objs per slab */

  18. /* 4) cache_grow/shrink */
  19.     /* order of pgs per slab (2^n) */
  20.     unsigned int gfporder;                         /× 每个slab包含的页框数,取2为底的对数 ×/

  21.     /* force GFP flags, e.g. GFP_DMA */
  22.     gfp_t gfpflags;                                /* 分配页框时传递给伙伴系统的标志 */

  23.     size_t colour;                                 /* 缓存的着色块个数 cache colouring range */
  24.     unsigned int colour_off;                       /* Slab基本的对齐偏移,基本偏移量×着色块个数得到的绝对偏移量 colour offset */
  25.     struct kmem_cache *slabp_cache;                /* Slab描述符在外部时使用,其指向的高速缓存用于存储描述符;在内部时为NULL */
  26.     unsigned int slab_size;                        /× Slab管理区大小,包含slab对象和kmem_buffctl_t数组 ×/
  27.     unsigned int dflags;                           /* 描述高速缓存的动态属性,目前没有使用 dynamic flags */

  28.     /* constructor func */
  29.     void (*ctor)(void *obj);                       /× 创造高速缓存时的构造函数指针 ×/

  30. /* 5) cache creation/removal */
  31.     const char *name;                              /× Cache的名字 ×/
  32.     struct list_head next;                         /× 链接高速缓存的双向指针至cache_chain上 ×/

  33. /* 6) statistics */
  34. #ifdef CONFIG_DEBUG_SLAB
  35.     unsigned long num_active;
  36.     unsigned long num_allocations;
  37.     unsigned long high_mark;
  38.     unsigned long grown;
  39.     unsigned long reaped;
  40.     unsigned long errors;
  41.     unsigned long max_freeable;
  42.     unsigned long node_allocs;
  43.     unsigned long node_frees;
  44.     unsigned long node_overflow;
  45.     atomic_t allochit;                                /× Cache的命中次数,在分配中更新 ×/
  46.     atomic_t allocmiss;                               /× Cache的未命中次数,在分配中更新 ×/
  47.     atomic_t freehit;
  48.     atomic_t freemiss;

  49.     /*
  50.      * If debugging is enabled, then the allocator can add additional
  51.      * fields and/or padding to every object. buffer_size contains the total
  52.      * object size including these internal fields, the following two
  53.      * variables contain the offset to the user object and its size.
  54.      */
  55.     int obj_offset;                                    /× 对象间的偏移 ×/
  56.     int obj_size;                                      /× 对象本身的大小 ×/
  57. #endif /* CONFIG_DEBUG_SLAB */

  58.     /*
  59.      * We put nodelists[] at the end of kmem_cache, because we want to size
  60.      * this array to nr_node_ids slots instead of MAX_NUMNODES
  61.      * (see kmem_cache_init())
  62.      * We still use [MAX_NUMNODES] and not [1] or [0] because cache_cache
  63.      * is statically defined, so we reserve the max number of nodes.
  64.      */
  65.     struct kmem_list3 *nodelists[MAX_NUMNODES];       /× 存放所有NUMA节点对应的相关数据,每个节点拥有自己的数据 ×/
  66.     /*
  67.      * Do not add fields after nodelists[]
  68.      */
  69. };

借鉴网友的图片,来加深下理解:

图基本说明了各结构之间的关联关系,具体申请和释放的过程,后面还会继续分析,结合图更容易理解。

另,下面一段摘抄自http://blog.csdn.net/hs794502825/article/details/7981524

每个slab首部都有一个小区域是不用的,称为“着色区”
着色区的大小使Slab中的每个对象的起始地址都按高速缓存中的缓存行(cache line)大小进行对齐(80386的一级高速缓存行大小为16字节,Pentium为32字节)。因为Slab是由1个页面或多个页面(最多为32)组成,因此每个Slab都是从一个页面边界开始的,它自然按高速缓存的缓冲行对齐。但是,Slab中的对象大小不确定,设置着色区的目的就是将Slab中第一个对象的起始地址往后推到与缓冲行对齐的位置。因为一个缓冲区中有多个Slab,因此,应该把每个缓冲区中的各个Slab着色区的大小尽量安排成不同的大小,这样可以使得在不同的Slab中,处于同一相对位置的对象,让它们在高速缓存中的起始地址相互错开,这样就可以改善高速缓存的存取效率。

每个Slab上最后一个对象以后也有个小小的废料区是不用的,这是对着色区大小的补偿,其大小取决于着色区的大小,以及Slab与其每个对象的相对大小。但该区域与着色区的总和对于同一种对象的各个Slab是个常数。

每个对象的大小基本上是所需数据结构的大小。只有当数据结构的大小不与高速缓存中的缓冲行对齐时,才增加若干字节使其对齐。所以,一个Slab上的所有对象的起始地址都必然是按高速缓存中的缓冲行对齐的。

与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。



阅读(1412) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~