内存管理[4]

     slab分配器扮演了通用数据结构缓存层的角色。

    slab分配器的概念首先在Sun Microsystem的SunOS 5.4操作系统中得以实现。Linux数据结构缓存层分享了它的名字和基本设计思想。

   slab分配器试图在几个基本原则之间寻求一种平衡:

频繁使用的数据结构也会频繁的分配和是否,因此应当适当缓存它们。

频繁分配和回收必然会导致内存碎片。为了避免这种现象,空闲链表的缓存会连续地存放。因为已释放的数据结构又会放回空闲链表,因此不会导致碎片。

回收的对象可以立即投入下一次分配,因此对于频繁的分配和释放,空闲链表能够提高其性能。

如果分配器知道对象大小、页大小和总的高速缓存的大小这样的概念,它会做出更明智的决策。

如果让部分缓存专属于单个处理器,那么,分配和释放和就可以在不加SMP锁的情况下进行。

如果分配器是NUMA相关的,它就可以从相同的内存节点为请求者进行分配。

对存放的对象进行着色,以防止多个对象映射到相同的高速缓存行。

  • slab层的设计

   slab层把不同的对象划分为所谓高速缓存组,其中每个高速缓存都存放不同类型的对象。每种对象类型对应一个高速缓存。kmalloc()接口建立在slab层之上,使用了一组通用高速缓存。即,kmalloc()分配的内存是从通用高速缓存中分配的。

   然后,这些高速缓存又被划分为slab。slab由一个或多个物理上连续的页组成。一般情况下,slab就仅仅由一页组成。每个高速缓存可以由多个slab组成。

    每个slab都包含一些对象成员,这里的对象指的是被缓存的数据结构。每个slab处于三种状态之一:满、部分满或空。一个满的slab没有空闲对象,一个空的slab没有分配出任何对象,一部分满的slab有一些对象已经分配出去还有些对象空闲中。当内核的某一部分需要一个新的对象时,先从部分满的slab中进行分配。如果没有部分满的slab就从空的slab进行分配。如果没有空的slab,就要创建一个slab了。

高速缓存、slab和对象之间的关系如下所示:

 

  1. 在<Slab.c(mm)>中
  2. /*
  3.  * struct kmem_cache
  4.  *
  5.  * manages a cache.
  6.  */
  7. struct kmem_cache {
  8. /* 1) per-cpu data, touched during every alloc/free */
  9.     struct array_cache *array[NR_CPUS];
  10. /* 2) Cache tunables. Protected by cache_chain_mutex */
  11.     unsigned int batchcount;
  12.     unsigned int limit;
  13.     unsigned int shared;
  14.     unsigned int buffer_size;
  15.     u32 reciprocal_buffer_size;
  16. /* 3) touched by every alloc & free from the backend */
  17.     struct kmem_list3 *nodelists[MAX_NUMNODES];
  18.     unsigned int flags;     /* constant flags */
  19.     unsigned int num;       /* # of objs per slab */
  20. /* 4) cache_grow/shrink */
  21.     /* order of pgs per slab (2^n) */
  22.     unsigned int gfporder;
  23.     /* force GFP flags, e.g. GFP_DMA */
  24.     gfp_t gfpflags;
  25.     size_t colour;          /* cache colouring range */
  26.     unsigned int colour_off;    /* colour offset */
  27.     struct kmem_cache *slabp_cache;
  28.     unsigned int slab_size;
  29.     unsigned int dflags;        /* dynamic flags */
  30.     /* constructor func */
  31.     void (*ctor) (void *, struct kmem_cache *, unsigned long);
  32.     /* de-constructor func */
  33.     void (*dtor) (void *, struct kmem_cache *, unsigned long);
  34. /* 5) cache creation/removal */
  35.     const char *name;
  36.     struct list_head next;
  37. /* 6) statistics */
  38. #if STATS
  39.     unsigned long num_active;
  40.     unsigned long num_allocations;
  41.     unsigned long high_mark;
  42.     unsigned long grown;
  43.     unsigned long reaped;
  44.     unsigned long errors;
  45.     unsigned long max_freeable;
  46.     unsigned long node_allocs;
  47.     unsigned long node_frees;
  48.     unsigned long node_overflow;
  49.     atomic_t allochit;
  50.     atomic_t allocmiss;
  51.     atomic_t freehit;
  52.     atomic_t freemiss;
  53. #endif
  54. #if DEBUG
  55.     /*
  56.      * If debugging is enabled, then the allocator can add additional
  57.      * fields and/or padding to every object. buffer_size contains the total
  58.      * object size including these internal fields, the following two
  59.      * variables contain the offset to the user object and its size.
  60.      */
  61.     int obj_offset;
  62.     int obj_size;
  63. #endif
  64. };

  每个高速缓存都是用kmem_cache_t结构来表示。这个结构包含三个链表slabs_full,slabs_partial和slabs_empty,均存放在kmem_list3结构内。这些链表包含高速缓存中的所有slab。

  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;
  9.     unsigned int free_limit;
  10.     unsigned int colour_next;   /* 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;       /* updated without locking */
  16. };

以下是slab的定义:

  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;
  10.     unsigned long colouroff;
  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;
  15. };

  slab描述符要么在slab之外另行分配,要么放在slab自身最开始的地方。如果slab描述符很小或者slab内部有足够的空间容纳slab描述符,那么描述符就存放在slab里面。

  slab分配器可以创建新的slab,这是通过alloc_pages_node()低级内核页分配器进行的:

 

  1. /*
  2.  * Interface to system's page allocator. No need to hold the cache-lock.
  3.  *
  4.  * If we requested dmaable memory, we will get it. Even if we
  5.  * did not request dmaable memory, we might get it, but that
  6.  * would be relatively rare and ignorable.
  7.  */
  8. static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
  9. {
  10.     struct page *page;
  11.     int nr_pages;
  12.     int i;
  13. #ifndef CONFIG_MMU
  14.     /*
  15.      * Nommu uses slab's for process anonymous memory allocations, and thus
  16.      * requires __GFP_COMP to properly refcount higher order allocations
  17.      */
  18.     flags |= __GFP_COMP;
  19. #endif
  20.     flags |= cachep->gfpflags;
  21.     page = alloc_pages_node(nodeid, flags, cachep->gfporder);
  22.     if (!page)
  23.         return NULL;
  24.     nr_pages = (1 << cachep->gfporder);
  25.     if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
  26.         add_zone_page_state(page_zone(page),
  27.             NR_SLAB_RECLAIMABLE, nr_pages);
  28.     else
  29.         add_zone_page_state(page_zone(page),
  30.             NR_SLAB_UNRECLAIMABLE, nr_pages);
  31.     for (i = 0; i < nr_pages; i++)
  32.         __SetPageSlab(page + i);
  33.     return page_address(page);
  34. }

  slab层只有当给定的高速缓存中既没有部分满也没有空的slab时才会调用页分配函数。而只有在下列情况下才会调用释放函数:当可用内存变得紧缺时,系统试图释放出更多内存以供使用,或者当高速缓存显式的被摧毁时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值