[Research] Linux Kernel Basic - (4) 커널에서의 메모리 할당 2편

[Research] Linux Kernel Basic - (4) 커널에서의 메모리 할당 2편

서론

해당 포스트에서는 리눅스 커널(Linux Kernel)에서도 연산을 수행하기 위해서 데이터가 저장되어야만 하는 공간이 있어야 한다. 이러한 공간의 주체는 메모리(memory)이며 리눅스 커널(Linux Kernel)에서의 메모리의 할당 방식을 알아본다. 포스트 작성에 앞서 오늘의 미팅에서 배웠던 이야기 주제로 자료조사를 진행하며 작성하며 b30W0lf님은 매우 잘 가르쳐주셨지만 무지성 침팬지 나의 두뇌 때문에 잘못된 내용이 있을 수 있음을 밝힌다.

리눅스 시스템에서의 메모리 할당

일반적인 리눅스 시스템의 경우는 물리적 메모리를 가지고 있으며 이를 프로세스가 사용할 수 있도록 페이지의 단위로 할당한다. 페이지의 경우는 일반적으로 4KB(0x1000)의 크기를 가지고 있다. 이는 alloc_pages() 함수를 이용해 할당받을 수 있다.

1
2
3
4
5
6
7
8
9
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
{
struct page *page;

page = alloc_pages(gfp_mask & ~__GFP_HIGHMEM, order);
if (!page)
return 0;
return (unsigned long) page_address(page);
}

해당 코드는 /mm/page_alloc.c에서 확인할 수 있다. 해당 코드 내부에 Line5에는 alloc_pages라는 함수를 확인할 수 있으며 이는 물리 메모리에서의 페이지를 할당 받는 함수이다. 해당 함수의 경우 파라미터로 gfp_mask와 order를 전달하는 것을 확인할 수 있다. 예를 들어 0을 넘기게될 경우 0x1000(4096) 메모리를 할당받고 1을 넘기게 될 경우 0x2000(8192) 메모리를 할당받고 2를 넘기게 될 경우 0x4000(16384)만큼의 메모리를 할당받는다. 이를 살펴보면 2의 제곱의 형태로 페이지를 할당받는 것을 확인할 수 있다. 왜 다음과 같은 형태로 메모리를 할당받을까?

와썹 버디!

이는 운영체제 수업에서 언급하였던 Buddy Memory Allocation과 관련이 있다고 판단된다. 원시적(primitive)인 방법을 통해서 메모리를 필요한 공간만큼 지속적으로 할당한 후 이를 더 이상 사용하지 않는다고 판단되어 해제할 경우 해당 프로세스의 메모리 만큼의 빈 공간(Hole)이 발생하며 지속적으로 이러한 형태들이 단편화가 진행된다면 빈 공간은 필요한 메모리 크기 이상으로 존재하나 적재될 수 없는 External Fragmentation의 상황이 발생한다.

Buddy Memory Allocator

이러한 문제점을 해결하기 위한 여러가지 방법이 있으나 지정된 크기를 짝을 지어 할당하거나 해제하여 다시 합치는 Buddy Memory Allocator가 있다. 일반적으로 현재의 시스템의 대다수는 Buddy Memory Allocator 형태로 메모리 관리를 진행한다. 이는 상단의 그림을 확인하면 보다 직관적으로 이해할 수 있다.

다음의 경우는 페이지의 단위(4KB)를 기준으로 메모리를 할당하기에 대용량의 버퍼를 사용하는 경우나 메모리 풀을 관리하는 경우에는 다음과 같은 형태로 메모리를 할당한다.

결론

다음은 페이지 단위로 메모리를 할당하는 리눅스 시스템을 확인하였고, 서로 짝을 지어 분할하거나 합쳐서 메모리를 해제하는 형태의 Buddy System Allocation을 확인하였다. 그렇다면 대용량의 버퍼가 아닌 커널에서의 구조체의 정보를 담기 위한 작은 사이즈(16Byte, 32Byte, …)등의 메모리를 할당하기 위해서는 어떠한 기법이 적용되어 있는 지 다음 포스트에서 확인해보겠다.