kmallocとvmallocの違い
kmalloc/vmallocは、両者とも仮想アドレスを取得するものですが、以下のような違いを有しています。
kmalocはオブジェクト単位(スラブ)で取得されます。このキャッシュは、kmaloc用として、8,16,32・・・4096,8192サイズで作成されており、引数で指定されるサイズの対応するスラブが取得されます。
従ってvmallocはクリティカルパスで使用できません。なおkmalocもそのキャッシュが無くなると、新規キャッシュのためページを取得することになり、kmalocの引数に、GFP_KERNEL/GFP_ATOMICを指定することで、クリティカルパスでの利用を可能としています。
kmalocはオブジェクト単位(スラブ)で取得されます。このキャッシュは、kmaloc用として、8,16,32・・・4096,8192サイズで作成されており、引数で指定されるサイズの対応するスラブが取得されます。
[ root@localhost kitamura]# cat /proc/slabinfo | grep kmalloc dma-kmalloc-4096 0 0 4096 dma-kmalloc-2048 0 0 2048 : dma-kmalloc-32 0 0 32 dma-kmalloc-16 0 0 16 dma-kmalloc-8 0 0 8 kmalloc-8192 36 36 8192 kmalloc-4096 114 128 4096 : kmalloc-64 3411 4032 64 kmalloc-32 7123 7808 32 kmalloc-16 6400 6400 16 kmalloc-8 8192 8192 8vmallocはページ単位で取得され、そのページは、その取得毎にアロケートしています。
従ってvmallocはクリティカルパスで使用できません。なおkmalocもそのキャッシュが無くなると、新規キャッシュのためページを取得することになり、kmalocの引数に、GFP_KERNEL/GFP_ATOMICを指定することで、クリティカルパスでの利用を可能としています。
void *__kmalloc(size_t size, gfp_t flags) { return __do_kmalloc(size, flags, __builtin_return_address(0)); } static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, void *caller) { struct kmem_cache *cachep; void *ret; サイズに合うスラブのキャッシュを取得 cachep = __find_general_cachep(size, flags); if (unlikely(ZERO_OR_NULL_PTR(cachep))) return cachep; 取得できなければ、キャッシュを取得して割り当てる ret = __cache_alloc(cachep, flags, caller); trace_kmalloc((unsigned long) caller, ret, size, cachep->buffer_size, flags); return ret; }
void *vmalloc(unsigned long size) { 最終的にGFP_KERNEL | __GFP_HIGHMEMで__vmalloc_node_rangeがコールされます。 return __vmalloc_node_flags(size, -1, GFP_KERNEL | __GFP_HIGHMEM); } void *__vmalloc_node_range(unsigned long size, unsigned long align, unsigned long start, unsigned long end, gfp_t gfp_mask, pgprot_t prot, int node, void *caller) { struct vm_struct *area; void *addr; unsigned long real_size = size; サイズをページアライメントに修正します。 size = PAGE_ALIGN(size); if (!size || (size >> PAGE_SHIFT) > totalram_pages) goto fail; サイズを満たすリニアアドレス空間を取得します。 area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNLIST, start, end, node, gfp_mask, caller); if (!area) goto fail; 上記リ割り当てたリニアアドレスに対応するページを割り当て、ページテーブルを設定します。 addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller); if (!addr) return NULL; vmlistをヘッダーとするリストにvmmallocで割り当てたareaをリストします。 insert_vmalloc_vmlist(area); kmemleak_objectにこのメモリオブジェクトをリストします。 CONFIG_DEBUG_KMEMLEAKでない時、未処理です。 kmemleak_alloc(addr, real_size, 3, gfp_mask); return addr;
fail: warn_alloc_failed(gfp_mask, 0, "vmalloc: allocation failure: %lu bytes\n", real_size); return NULL; }