把握linux内核设计思想(十二):内存管理之slab分配器
【版權聲明:尊重原創,轉載請保留出處:blog.csdn.net/shallnet,文章僅供學習交流。請勿用于商業用途】
???????上一節最后說到對于小內存區的請求,假設採用伙伴系統來進行分配,則會在頁內產生非常多空暇空間無法使用。因此產生slab分配器來處理對小內存區(幾十或幾百字節)的請求。Linux中引入Slab的主要目的是為了降低對伙伴算法的調用次數。
????????內核常常重復使用某一內存區。比如。僅僅要內核創建一個新的進程,就要為該進程相關的數據結構(task_struct、打開文件對象等)分配內存區。當進程結束時。收回這些內存區。由于進程的創建和撤銷很頻繁。linux把那些頻繁使用的頁面保存在快速緩存中并又一次使用。
????????slab分配器基于對象進行管理,同樣類型的對象歸為一類(如進程描寫敘述符就是一類),每當要申請這樣一個對象。slab分配器就分配一個空暇對象出去,而當要釋放時,將其又一次保存在slab分配器中,而不是直接返回給伙伴系統。
對于頻繁請求的對象。創建適當大小的專用對象來處理。對于不頻繁的對象。用一系列幾何分布大小的對象來處理(詳見通用對象)。
????????Slab分配模式把對象分組放進緩沖區,為緩沖區的組織和管理與硬件快速緩存的命中率密切相關,因此。Slab緩沖區并不是由各個對象直接構成。而是由一連串的“大塊(Slab)”構成,而每一個大塊中則包括了若干個同種類型的對象。這些對象或已被分配?;蚩障?。實際上。緩沖區就是主存中的一片區域,把這片區域劃分為多個塊。每塊就是一個Slab,每一個Slab由一個或多個頁面組成,每一個Slab中存放的就是對象。
slab相關數據結構:
緩沖區數據結構使用kmem_cache結構來表示。
struct kmem_cache { /* 1) per-cpu data, touched during every alloc/free */struct array_cache *array[NR_CPUS]; /* 2) Cache tunables. Protected by cache_chain_mutex */unsigned int batchcount;unsigned int limit;unsigned int shared;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 */struct kmem_cache *slabp_cache;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;/* 6) statistics */ #ifdef CONFIG_DEBUG_SLABunsigned 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 allocator can 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 user object and its size.*/int obj_offset;int obj_size; #endif /* CONFIG_DEBUG_SLAB *//** We put nodelists[] at the end of kmem_cache, because we want to size* this array to nr_node_ids slots instead of MAX_NUMNODES* (see kmem_cache_init())* We still use [MAX_NUMNODES] and not [1] or [0] because cache_cache* is statically defined, so we reserve the max number of nodes.*/struct kmem_list3 *nodelists[MAX_NUMNODES];/** Do not add fields after nodelists[]*/ };
當中struct kmem_list3結構體鏈接slab,共享快速緩存。其定義例如以下:
/** The slab lists for all objects.*/ struct kmem_list3 {struct list_head slabs_partial; /* partial list first, better asm code */struct list_head slabs_full;struct list_head slabs_free;unsigned long free_objects;unsigned int free_limit;unsigned int colour_next; /* Per-node cache coloring */spinlock_t list_lock;struct array_cache *shared; /* shared per node */struct array_cache **alien; /* on other nodes */unsigned long next_reap; /* updated without locking */int free_touched; /* updated without locking */ };
該結構包括三個鏈表:slabs_partial、slabs_full、slabs_free,這些鏈表包括緩沖區全部slab。slab描寫敘述符struct slab用于描寫敘述每一個slab:
/** struct slab** Manages the objs in a slab. Placed either at the beginning of mem allocated* for a slab, or allocated from an general cache.* Slabs are chained into three list: fully used, partial, fully free slabs.*/ struct slab {struct list_head list;unsigned long colouroff;void *s_mem; /* including colour offset */unsigned int inuse; /* num of objs active in slab */kmem_bufctl_t free;unsigned short nodeid; };
一個新的緩沖區使用例如以下函數創建:
struct kmem_cache *kmem_cache_create (const char *name, size_t size, size_t align, unsigned long flags, void (*ctor)(void *));函數創建成功會返回一個指向所創建緩沖區的指針;撤銷一個緩沖區調用例如以下函數:
<span style="font-family:Microsoft YaHei;">void kmem_cache_destroy(struct kmem_cache *cachep);</span>上面兩個函數都不能在中斷上下文中使用。由于它可能睡眠。
在創建來緩沖區之后,能夠通過下列函數獲取對象:
/*** kmem_cache_alloc - Allocate an object* @cachep: The cache to allocate from.* @flags: See kmalloc().** Allocate an object from this cache. The flags are only relevant* if the cache has no available objects.*/ void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags) {void *ret = __cache_alloc(cachep, flags, __builtin_return_address(0));trace_kmem_cache_alloc(_RET_IP_, ret,obj_size(cachep), cachep->buffer_size, flags);return ret; }
該函數從給點緩沖區cachep中返回一個指向對象的指針。
假設緩沖區的全部slab中都沒有空暇對象,那么slab層必須通過kmem_getpages()獲取新的頁。參數flags傳遞給_get_free_pages()。
<span style="font-family:Microsoft YaHei;">static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid);</span>釋放對象使用例如以下函數:
/*** kmem_cache_free - Deallocate an object* @cachep: The cache the allocation was from.* @objp: The previously allocated object.** Free an object which was previously allocated from this* cache.*/ void kmem_cache_free(struct kmem_cache *cachep, void *objp) {unsigned long flags;local_irq_save(flags);debug_check_no_locks_freed(objp, obj_size(cachep));if (!(cachep->flags & SLAB_DEBUG_OBJECTS))debug_check_no_obj_freed(objp, obj_size(cachep));__cache_free(cachep, objp);local_irq_restore(flags);trace_kmem_cache_free(_RET_IP_, objp); }
假設你要頻繁的創建非常多同樣類型的對象,就要當考慮使用slab快速緩存區。
實際上上一節所講kmalloc()函數也是使用slab分配器分配的。
static __always_inline void *kmalloc(size_t size, gfp_t flags) {struct kmem_cache *cachep;void *ret;if (__builtin_constant_p(size)) {int i = 0;if (!size)return ZERO_SIZE_PTR;#define CACHE(x) \if (size <= x) \goto found; \else \i++; #include <linux/kmalloc_sizes.h> #undef CACHEreturn NULL; found: #ifdef CONFIG_ZONE_DMAif (flags & GFP_DMA)cachep = malloc_sizes[i].cs_dmacachep;else #endifcachep = malloc_sizes[i].cs_cachep;ret = kmem_cache_alloc_notrace(cachep, flags);trace_kmalloc(_THIS_IP_, ret,size, slab_buffer_size(cachep), flags);return ret;}return __kmalloc(size, flags); }kfree函數實現例如以下:
/*** kfree - free previously allocated memory* @objp: pointer returned by kmalloc.** If @objp is NULL, no operation is performed.** Don't free memory not originally allocated by kmalloc()* or you will run into trouble.*/ void kfree(const void *objp) {struct kmem_cache *c;unsigned long flags;trace_kfree(_RET_IP_, objp);if (unlikely(ZERO_OR_NULL_PTR(objp)))return;local_irq_save(flags);kfree_debugcheck(objp);c = virt_to_cache(objp);debug_check_no_locks_freed(objp, obj_size(c));debug_check_no_obj_freed(objp, obj_size(c));__cache_free(c, (void *)objp);local_irq_restore(flags); } 最后。結合上一節。看看分配函數的選擇:
假設須要連續的物理頁,就能夠使用某個低級頁分配器或kmalloc()。
假設想從高端內存進行分配,使用alloc_pages()。
假設不須要物理上連續的頁,而不過虛擬地址上連續的頁,那么就是用vmalloc。
假設要創建和銷毀非常多大的數據結構,那么考慮建立slab快速緩存。
總結
以上是生活随笔為你收集整理的把握linux内核设计思想(十二):内存管理之slab分配器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是社保卡
- 下一篇: zepp life怎么设置闹钟