本文轉載自:http://blog.chinaunix.net/uid-26859697-id-5573776.html
kmalloc()是基于slab/slob/slub分配分配算法上實現的,不少地方將其作為slab/slob/slub分配算法的入口,實際上是略有區別的。
現在分析一下其實現:
【file:/include/linux/slab.h】/**?*?kmalloc?-?allocate memory?*?@size:?how many bytes of memory are required.?*?@flags:?the type of memory?to?allocate.?*?*?kmalloc?is?the normal method of allocating memory?*?for?objects smaller than page size?in?the kernel.?*?*?The @flags argument may be one of:?*?*?%GFP_USER?-?Allocate memory?on?behalf of user.?May sleep.?*?*?%GFP_KERNEL?-?Allocate normal kernel ram.?May sleep.?*?*?%GFP_ATOMIC?-?Allocation will?not?sleep.?May use emergency pools.?*?For?example,?use this inside interrupt handlers.?*?*?%GFP_HIGHUSER?-?Allocate pages from high memory.?*?*?%GFP_NOIO?-?Do?not?do?any I/O at all?while?trying?to?get?memory.?*?*?%GFP_NOFS?-?Do?not?make any fs calls?while?trying?to?get?memory.?*?*?%GFP_NOWAIT?-?Allocation will?not?sleep.?*?*?%__GFP_THISNODE?-?Allocate node-local memory only.?*?*?%GFP_DMA?-?Allocation suitable?for?DMA.?*?Should only be used?for?kmalloc()?caches.?Otherwise,?use a?*?slab created with SLAB_DMA.?*?*?Also it?is?possible?to?set?different flags by?OR'ing?*?in?one?or?more of the following additional @flags:?*?*?%__GFP_COLD?-?Request cache-cold pages instead of?*?trying?to?return cache-warm pages.?*?*?%__GFP_HIGH?-?This allocation has high priority?and?may use emergency pools.?*?*?%__GFP_NOFAIL?-?Indicate that this allocation?is?in?no way allowed?to?fail?*?(think twice before using).?*?*?%__GFP_NORETRY?-?If?memory?is?not?immediately available,?*?then?give up at once.?*?*?%__GFP_NOWARN?-?If?allocation fails,?don't issue any warnings.?*?*?%__GFP_REPEAT?-?If?allocation fails initially,?try once more before failing.?*?*?There are other flags available as well,?but these are?not?intended?*?for?general use,?and?so are?not?documented here.?For?a full list of?*?potential flags,?always refer?to?linux/gfp.h.?*/static __always_inline void?*kmalloc(size_t size,?gfp_t flags){????if?(__builtin_constant_p(size))?{????????if?(size?>?KMALLOC_MAX_CACHE_SIZE)????????????return kmalloc_large(size,?flags);#ifndef CONFIG_SLOB????????if?(!(flags?&?GFP_DMA))?{????????????int?index?=?kmalloc_index(size);?????????????if?(!index)????????????????return ZERO_SIZE_PTR;?????????????return kmem_cache_alloc_trace(kmalloc_caches[index],????????????????????flags,?size);????????}#endif????}????return __kmalloc(size,?flags);} ?
kmalloc()的參數size表示申請的空間大小,而flags則表示分配標志。kamlloc的分配標志眾多,各標志都分配標識特定的bit位,藉此可以多樣組合。
GFP_USER:用于表示為用戶空間分配內存,可能會引起休眠;
GFP_KERNEL:內核內存的常規分配,可能會引起休眠;
GFP_ATOMIC:該分配不會引起休眠,但可能會使用應急內存資源,通常用于中斷處理中;
GFP_HIGHUSER:使用高端內存進行分配;
GFP_NOIO:分配內存時,禁止任何IO操作;
GFP_NOFS:分配內存時,禁止任何文件系統操作;
GFP_NOWAIT:分配內存時禁止休眠;
__GFP_THISNODE:分配內存時,僅從本地節點內存中分配;
GFP_DMA:從DMA內存中分配合適的內存,應僅使用于kmalloc的cache分配;
__GFP_COLD:用于請求分配冷熱頁中的冷頁;
__GFP_HIGH:用于表示該分配優先級較高并可能會使用應急內存資源;
__GFP_NOFAIL:用于指示該分配不允許分配失敗,該標志需要慎用;
__GFP_NORETRY:如果分配內存未能夠直接獲取到,則不再嘗試分配,直接放棄;
__GFP_NOWARN:如果分配過程中失敗,不上報任何告警;
__GFP_REPEAT:如果分配過程中失敗,則嘗試再次申請;
函數入口if判斷內的__builtin_constant_p是Gcc內建函數,用于判斷一個值是否為編譯時常量,是則返回true,否則返回false。也就意味著如果調用kmalloc()傳入常量且該值大于KMALLOC_MAX_CACHE_SIZE(即申請空間超過kmalloc()所能分配最大cache的大小),那么將會通過kmalloc_large()進行分配;否則都將通過__kmalloc()進行分配。如果通過kmalloc_large()進行內存分配,將會經kmalloc_large()->kmalloc_order()->__get_free_pages(),最終通過Buddy伙伴算法申請所需內存。
伙伴算法前面已經分析過了,不再贅述,接下來看__kmalloc()的實現:
【file:/mm/slub.c】void?*__kmalloc(size_t size,?gfp_t flags){????struct kmem_cache?*s;????void?*ret;?????if?(unlikely(size?>?KMALLOC_MAX_CACHE_SIZE))????????return kmalloc_large(size,?flags);?????s?=?kmalloc_slab(size,?flags);?????if?(unlikely(ZERO_OR_NULL_PTR(s)))????????return s;?????ret?=?slab_alloc(s,?flags,?_RET_IP_);?????trace_kmalloc(_RET_IP_,?ret,?size,?s->size,?flags);?????return ret;} ?
該函數同樣判斷申請是否超過最大cache大小,如果是則通過kmalloc_large()進行分配;接著通過申請大小及申請標志調用kmalloc_slab()查找適用的kmem_cache;最后通過slab_alloc()進行slab分配。
具體看一下kmalloc_slab()的實現:
【file:/mm/slab_commmon.c】/*?*?Find the kmem_cache structure that serves a given size of?*?allocation?*/struct kmem_cache?*kmalloc_slab(size_t size,?gfp_t flags){????int?index;?????if?(unlikely(size?>?KMALLOC_MAX_SIZE))?{????????WARN_ON_ONCE(!(flags?&?__GFP_NOWARN));????????return?NULL;????}?????if?(size?<=?192)?{????????if?(!size)????????????return ZERO_SIZE_PTR;?????????index?=?size_index[size_index_elem(size)];????}?else????????index?=?fls(size?-?1);?#ifdef CONFIG_ZONE_DMA????if?(unlikely((flags?&?GFP_DMA)))????????return kmalloc_dma_caches[index];?#endif????return kmalloc_caches[index];} ?
如果申請的大小超過KMALLOC_MAX_SIZE最大值,則返回NULL表示失敗;如果申請大小小于192,且不為0,將通過size_index_elem宏轉換為下標后,經size_index全局數組取得索引值,否則將直接通過fls()取得索引值;最后如果開啟了DMA內存配置且設置了GFP_DMA標志,將結合索引值通過kmalloc_dma_caches返回kmem_cache管理結構信息,否則將通過kmalloc_caches返回該結構。
由此可以看出kmalloc()實現較為簡單,起分配所得的內存不僅是虛擬地址上的連續存儲空間,同時也是物理地址上的連續存儲空間。這是有別于后面將會分析到的vmalloc()申請所得的內存。
此外再過一下kfree()的接口實現,該函數在多處均有實現,主要是在slab.c/slob.c/slub.c中,所以也說kmalloc()和kfree()是基于slab/slob/slub實現的。這里接前面的slub算法,主要分析一下slub.c中的kfree()實現:
【file:/mm/slub.c】void kfree(const?void?*x){????struct page?*page;????void?*object?=?(void?*)x;?????trace_kfree(_RET_IP_,?x);?????if?(unlikely(ZERO_OR_NULL_PTR(x)))????????return;?????page?=?virt_to_head_page(x);????if?(unlikely(!PageSlab(page)))?{????????BUG_ON(!PageCompound(page));????????kfree_hook(x);????????__free_memcg_kmem_pages(page,?compound_order(page));????????return;????}????slab_free(page->slab_cache,?page,?object,?_RET_IP_);} ?
該函數實現簡單,首先是經過trace_kfree()記錄kfree軌跡,然后if (unlikely(ZERO_OR_NULL_PTR(x)))對地址做非零判斷,接著virt_to_head_page(x)將虛擬地址轉換到頁面;再是判斷if (unlikely(!PageSlab(page)))判斷該頁面是否作為slab分配管理,如果是的話則轉為通過slab_free()進行釋放,否則將進入if分支中;在if分支中,將會kfree_hook()做釋放前kmemleak處理(該函數主要是封裝了kmemleak_free()),完了之后將會__free_memcg_kmem_pages()將頁面釋放,同時該函數內也將cgroup釋放處理。
kmalloc()和kfree()也就這么簡單了。
轉載于:https://www.cnblogs.com/zzb-Dream-90Time/p/9023118.html
總結
以上是生活随笔為你收集整理的Linux-3.14.12内存管理笔记【kmalloc与kfree实现】【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。