kvmalloc()
함수는 리눅스 커널에서 메모리를 동적으로 할당하는 여러 함수 중 하나이다. 이 함수는 kmalloc()
와 vmalloc()
의 장점을 결합하여, 필요한 경우 페이지별로 물리적 메모리를 할당하는 기능을 제공한다. 이 글에서는 kvmalloc() 함수의 사용법과 내부 작동 방식에 대해 알아본다.
kvmalloc()
함수란?
이 함수는 요청한 메모리 크기에 따라 kmalloc()
또는 vmalloc()
를 선택적으로 사용하여 효율적으로 메모리를 할당한다.
- 작은 메모리 할당:
kmalloc()
를 사용하여 물리적으로 연속된 메모리를 할당한다. - 큰 메모리 할당:
vmalloc()
를 사용하여 가상적으로 연속된 메모리를 할당한다.
kvmalloc()
함수
kvmalloc()
함수는 다음과 같이 define 되어 있다.
void *kvmalloc(size_t size, gfp_t flags);
size
: 할당할 메모리의 크기 (바이트 단위)flags
: 메모리 할당 플래그 (GFP_KERNEL, GFP_ATOMIC 등)
사용 예제
다음은 커널 드라이버 예제인데, kvmalloc()
함수를 사용하여 1KB의 메모리를 할당하고 이를 해제하는 간단한 작업을 수행하는 코드 이다.
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
static int __init my_module_init(void)
{
void *ptr;
size_t size = 1024; // 할당할 메모리 크기 (1KB)
// kvmalloc()을 사용하여 메모리 할당
ptr = kvmalloc(size, GFP_KERNEL);
if (!ptr) {
pr_err("kvmalloc failed\n");
return -ENOMEM;
}
pr_info("kvmalloc succeeded\n");
// 할당된 메모리 사용 (예: 초기화)
memset(ptr, 0, size);
// 할당된 메모리 해제
kvfree(ptr);
return 0;
}
static void __exit my_module_exit(void)
{
pr_info("Module exit\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("kvmalloc example");
kvmalloc()
함수의 내부 작동 방식
kvmalloc()
선언은 다음과 같다.
#define kvmalloc(_size, _flags) kvmalloc_node(_size, _flags, NUMA_NO_NODE)
#define kvmalloc_noprof(_size, _flags) kvmalloc_node_noprof(_size, _flags, NUMA_NO_NODE)
#define kvzalloc(_size, _flags) kvmalloc(_size, (_flags)|__GFP_ZERO)
#define kvzalloc_node(_size, _flags, _node) kvmalloc_node(_size, (_flags)|__GFP_ZERO, _node)
실제 kvmalloc()
실행 함수인 kvmalloc_node()
함수이다.
/**
* 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);
kvmalloc_node()
함수는 내부적으로 다음과 같은 단계를 거친다.
- 크기 확인: 할당할 메모리 크기를 확인한다.
- kmalloc() 시도: 할당할 메모리 크기가 작은 경우,
kmalloc()
을 사용하여 물리적으로 연속된 메모리를 할당한다. - vmalloc() 사용: 할당할 메모리 크기가 큰 경우,
vmalloc()
을 사용하여 가상적으로 연속된 메모리를 할당한다. - 메모리 반환: 성공적으로 메모리가 할당되면 포인터를 반환하고, 실패하면 NULL을 반환한다.
무분별한 큰 사이즈를 kmalloc()
을 사용하다보면, 메모리 단편화로 인한 메모리 효율이 떨어져, 이로 인한 부하가 가중되고, 메모리가 있음에도 OOM이 발생할 수 있다. kvmalloc()
함수는 kmalloc()
와 vmalloc()
의 장점을 결합하여 작은 메모리와 큰 메모리 할당을 모두 효율적으로 처리할 수 있다.