KVMALLOC()
memory = kmalloc(allocation_size, GFP_KERNEL); if (!memory) memory = vmalloc(allocation_size);
kvmalloc() 은 내부적으로 kmalloc() 호출을 시도한다. 이는 slab allocator 를 이용한 메모리 압박이 없다면 빠른 메모리 할당을 지원한다. 또한 slab 은 PAGE_SIZE(32비트에서는 4KB) 보다 작은 메모리 할당을 위해 사용하고, 그보다 큰경우에는 물리적으로 연속적인 메모리를 할당하려고 한다. 하지만, 시스템을 운영하다 보면, 할당할 수 있는 메모리 공간은 있지만, 단편화로 인해 물리적으로 연속적인 공간은 할당 받지 못하는 경우가 있다. 물론, 연속적인 공간 확보를 위한 compaction 등의 feature 로 노력은 하지만 연속적인 공간 요청이 크다면 힘들 수도 있다.
이런 경우 가상주소 공간에서 연속적인 메모리 할당을 vmalloc() 으로 가능하게 한다. vmalloc() 은 가상 주소 공간에서 연속적이지만, 실제 물리적으로는 흩어진 메모리를 관리한다. 이런 할당은 페이지 테이블의 수정이 생기고, TLB cache 의 invalidation 을 갖게 된다.(페이지 폴트) 또한 PAGE_SIZE 보다 작은 메모리 할당은 align 되어 PAGE_SIZE 만큼 할당할 것이다.
이제 kvmalloc() 내부를 보자.
/** * kvmalloc_node - attempt to allocate physically contiguous memory, but upon * failure, fall back to non-contiguous (vmalloc) allocation. * @size: size of the request. * @flags: gfp mask for the allocation - must be compatible (superset) with GFP_KERNEL. * @node: numa node to allocate from * * Uses kmalloc to get the memory but if the allocation fails then falls back * to the vmalloc allocator. Use kvfree for freeing the memory. * * Reclaim modifiers - __GFP_NORETRY and __GFP_NOFAIL are not supported. __GFP_REPEAT * is supported only for large (>32kB) allocations, and it should be used only if * kmalloc is preferable to the vmalloc fallback, due to visible performance drawbacks. * * Any use of gfp flags outside of GFP_KERNEL should be consulted with mm people. */ void *kvmalloc_node(size_t size, gfp_t flags, int node) { gfp_t kmalloc_flags = flags; void *ret; /* * vmalloc uses GFP_KERNEL for some internal allocations (e.g page tables) * so the given set of flags has to be compatible. */ WARN_ON_ONCE((flags & GFP_KERNEL) != GFP_KERNEL); /* * Make sure that larger requests are not too disruptive - no OOM * killer and no allocation failure warnings as we have a fallback */ if (size > PAGE_SIZE) { kmalloc_flags |= __GFP_NOWARN; /* * We have to override __GFP_REPEAT by __GFP_NORETRY for !costly * requests because there is no other way to tell the allocator * that we want to fail rather than retry endlessly. */ if (!(kmalloc_flags & __GFP_REPEAT) || (size <= PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) kmalloc_flags |= __GFP_NORETRY; } ret = kmalloc_node(size, kmalloc_flags, node); /* * It doesn't really make sense to fallback to vmalloc for sub page * requests */ if (ret || size <= PAGE_SIZE) return ret; return __vmalloc_node_flags(size, node, flags); } EXPORT_SYMBOL(kvmalloc_node);
간단히, 메모리 할당이 8 개 페이지 크기보다 같거나 작다면, vmalloc() 은 쓰지 않을 것이다. 그냥 될때까지(oom killer 도 돌고 요청완료할 때까지) 진행을 하고, 그 크기보다 클때는 vmalloc() 으로 메모리 할당을 대체 하겠다는 것이다.
이와 관련된 헬퍼 함수는 다음과 같다.
void *kvmalloc(size_t size, gfp_t flags); void *kvzalloc(size_t size, gfp_t flags); void *kvmalloc_node(size_t size, gfp_t flags, int node); void *kvzalloc_node(size_t size, gfp_t flags, int node);
커널 코드를 보다가 이 헬퍼 함수로 교체 가능한 코드가 있다면 수정 하면 좋겠다.
'Linux Kernel Study' 카테고리의 다른 글
[책 소개] 리눅스 커널 패치와 커밋 (1) | 2015.04.17 |
---|---|
Qemu booted kernel debugging with GDB (0) | 2013.11.22 |
[Kernel] current/get_current macro 로 task 정보 얻는 과정. (0) | 2013.10.25 |
[Kernel] define 으로 do { ... } while(0) 많이 쓰는 이유 (2) | 2013.10.22 |
Kernel Likely/Unlikely (2) | 2013.10.21 |