본문 바로가기

Server, DevOps/LINUX , OS

palloc과 malloc의 차이점: 메모리 할당 구조 이해하기

들어가며

Pintos 프로젝트의 4주간 여정이 마무리되었습니다. 흥미로운 부분도 많았지만, 모르는 것이 너무 많아 OS라는 깊은 바다에 빠져 허우적대는 기분이 들기도 했습니다. 그래서 4주 전으로 돌아가 다시 한 번 해보면 더 많은 것을 배울 수 있었을 텐데 하는 아쉬움도 남습니다. 지금이라도 배운 내용을 정리하며 완전히 내 것으로 만들기 위해, 이번 시간을 활용해 메모리 할당에 대해 글을 작성해보려 합니다. 특히, 처음 Virtual Memory 과제를 접하면서 malloc과 palloc의 개념이 혼동되었던 경험을 다루어 보겠습니다.

 

 

위의 함수는 page를 초기화하는 함수입니다. 처음 빨간 줄의 코드를 보았을 때, pagepalloc_get_page()으로 할당하는게 맞다고 생각했지만, malloc으로 메모리를 할당하고 있다는 점이 혼란스러웠습니다. 그렇다면 언제 malloc을 사용하고 언제 palloc을 사용하는걸까요? 일단, malloc과 palloc의 기본 개념부터 정리해 보겠습니다.

 

palloc과 malloc의 기본 개념

palloc

palloc은 페이지 단위로 메모리를 할당하는 함수입니다. 페이지는 운영체제 메모리 관리의 기본 단위로 보통 4KB의 크기를 가집니다. pintos에서는 palloc_get_page(PAL_USER)를 호출하면 4KB 단위의 메모리를 확보합니다. 이 메모리는 실제 데이터가 저장될 공간으로 사용되고, stack 영역에 공간을 할당됩니다.

malloc

malloc은 사용자가 지정한 크기만큼 메모리를 동적으로 할당합니다. 주로 구조체나 작은 데이터 블록을 관리할 때 사용됩니다.

 

그래서 위의 코드에서 page 자체는 구조체이기에 malloc으로 할당한 것이었습니다. malloc은 heap영역에서 필요한 만큼만 메모리를 할당하기에, 구조체나 작은 크기의 데이터 저장을 위해 적합하다는 것을 알게 되었습니다! 그렇다면 palloc은 어떤 방식으로, 언제 할당이 되는걸까요? pintos에서 palloc_get_page()의 동작 흐름을 살펴보겠습니다.

 

palloc의 내부 동작 원리

palloc_get_multiple()는 비트맵을 사용하여 연속된 페이지를 관리합니다. 함수의 주요 로직을 도식화 해보았습니다.

 

- 먼저, 유저 공간에서의 할당인지를 확인한 후, 맞는 pool 구조체를 할당합니다.

- 그 후, 페이지를 할당해주기 위해 page_idx를 확인하는데요. 왜 bitmap을 사용할까요?

 

비트맵을 도식화해보았습니다. 각 칸이 4KB의 한 페이지를 나타내며, 이 페이지의 사용 여부는 비트로 표시됩니다. 비트맵에서 필요한 페이지 수(cnt)만큼의 연속된 공간을 찾고, 0을 1로 flip해 해당 페이지를 사용하도록 표시합니다. 그리고, 첫 번째 공간의 인덱스를 반홥니다. 만약 PAL_ZERO 옵션이 주어진다면, 페이지 수만큼 0으로 초기화합니다. 이렇게 palloc_get_multiple()은 연속된 페이지를 효율적으로 찾고 관리하고 있습니다.

 

그런데 또 비트맵의 크기는 어떻게 정해지는걸까요?

아래의 코드를 확인해보겠습니다.

static void
init_pool (struct pool *p, void **bm_base, uint64_t start, uint64_t end) {

    uint64_t pgcnt = (end - start) / PGSIZE;
    size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (pgcnt), PGSIZE) * PGSIZE;

    lock_init(&p->lock);
    p->used_map = bitmap_create_in_buf (pgcnt, *bm_base, bm_pages);
    p->base = (void *) start;

    bitmap_set_all(p->used_map, true);

    *bm_base += bm_pages;
}

 

init_pool()은 유저 메모리 공간을 초기화할 때 비트맵도 함께 생성합니다. pgcnt는 비트맵에서 관리할 페이지의 개수, bm_base는 비트맵이 생성될 메모리 버퍼의 주소, bm_pages는 비트맵을 저장하는 데 필요한 메모리 크기를 나타냅니다.

 

비트맵의 크기는 메모리에서 관리해야 하는 전체 페이지 수(end-start)에 따라 결정되고 있음을 알 수 있는데요. 핀토스에선 스택 제한이 1MB이므로, 256개의 비트를 사용해 각 페이지의 사용 여부를 기록합니다. 즉, 1MB = 256 페이지 (4KB 페이지 기준)를 관리할 수 있는 크기입니다.

 

왜 구조체는 힙에서 관리하고, 페이지는 페이지 단위로 관리할까?

 

구조체는 몇 바이트에서 몇십 바이트 정도의 작은 크기를 가지므로 힙 메모리를 사용하는 것이 더 적합합니다. 힙은 동적 메모리 할당이 가능하며, 개발자가 필요한 만큼 메모리를 할당하고 해제할 수 있습니다. 메모리 공간을 효율적으로 사용할 수 있죠. 그래서 작은 frame 구조체는 malloc으로 동적 할당 해줍니다.

 

반면, 페이지는 메모리 관리의 기본 단위로 사용됩니다. 페이지 단위로 메모리를 할당 및 관리하고, 메모리 보호 및 효율성을 제공하기에 가상 메모리 시스템에서는 페이지 단위로 메모리 할당을 하게 됩니다. 따라서 페이지 공간을 나타내는 *kva같은 가상 주소는 palloc_get_page()로 할당을 해주는 것이죠.

 

 

Pintos의 특이점

그런데 여기서 pintos만의 다른 점이 있습니다. pintos는 메모리 동적 할당시 OS-level memory allocator를 사용하는 구조가 아닙니다. 별도의 heap 영역이 따로 구성되어있지않기 때문에, malloc이 메모리를 할당할 때, heap을 사용하는 대신 stack에 저장하는 방식을 사용하고 있습니다. 그러므로 위에서 한 설명은 일반적인 OS기준으로 위의 설명을 했다는 것을 참조해주세요!

 

마무리하며

 

Pintos VM프로젝트를 통해 메모리 관리의 기본 원리와 OS의 메모리 할당 방식에 대해 이해할 수 있었습니다. 처음에는 생소하고 어려운 개념들이 많아 힘들었지만, 하나씩 피그마를 통해 정리하고, 이해하는 과정을 통해 많은 성장을 느낄 수 있었습니다. 이번 글에서는 malloc과 palloc의 차이점을 파악하고, 페이지 단위의 메모리 할당이 운영체제에서 어떻게 효율적으로 관리되는지를 소개했습니다. 다음 글에서는 page fault나 swap에 대해 소개해보겠습니다!👻

 

반응형