리눅스 커널: Memory Management

1231·2026년 6월 28일

가상 주소, 물리주소, struct page

1MB 단위의 섹션으로...4096개를 나눔. -> 4GB

이를 PDE, Page Directory Entry 로 커버.
접근했을때 flag가 01이면 2차 테이블, PTE를 찾아가야하고,
10이면 해당 섹션, 1MB가 모두 채워져있다는뜻, offset만 추가하면 된다. 바로 물리 주소에 접근 가능.
-> 1MB 연속으로 존재할때만 물리적으로 할당, Buddy Allocator 를 통함..

31:20 = Level 1 Index, 4096 개의 Index, 각 1MB
19:12 = Level 2 Index, 256 개의 Index, 각 4096Byte(Small의 경우)
19:16 = Large의 경우, 64KB짜리 page.

struct folio
대형 페이지(Compound Page)의 진짜 대가리(Head) 주소만을 가리키도록 강제하는 타입 안전장치다. 내부적으로 struct page와 메모리 공간을 공유(Union)한다.

차이점은 낱개 4KB 단위로 접근할 때마다 진짜 대가리 주소를 찾기 위해 커널 내부에서 반복적으로 호출되던 compound_head() 같은 포인터 추적 연산 오버헤드를 원천 차단하여 시스템 전반의 성능을 끌어올린다는 점.

그래도 여전히 4kb 짜리 페이지 하나마다 struct page 매핑은 필수적이다.
-> 이를 최적화하기 위해 folio slab등의 이용, struct page 자체의 크기 감축 시도, HAVE_ARCH_PFN_VALID 등의 시도가 있음.

Zone

free_area

11개의 Order
순서대로 2^0, 2^1 ... 2^10 개의 연속된 4KB Page의 개수
즉, Normal zone같은 경우 2^6. 7번째를 보면 총 451개의 연속된 1MB 메모리가 free상태인것을 확인할 수 있다.

include/linux/mmzone.h

struct zone 의 필드에는 다음과 같이 정의되어있음.


연속된 페이지들의 가장 앞 page만 tail에 등록되게 된다.

Page를 Free할때 실행되는 코드
mm/page_alloc.c

while부분을 잘 보자. find_buddy_page_pfn으로 buddy page 를 찾는다. 만약, 더이상 buddy를 찾을 수 없는 회차의 경우 루프를 중지하고 done_merging으로 가서 merge를 수행한다.

다음은 done_merging으로 가지 않고...buddy를 찾았을때 실행되는 루프 코드이다.

이때, & AND 연산에 의해서 combined_pfn은 buddy_pfn와 pfn중에 더 낮은 주소가 되고,

page 포인터는 pfn이 buddy_pfn보다 높은 경우, combined_pfn은 buddy_pfn이 되었을 것이므로, 그에 맞게 page 포인터도 아래로 옮겨야한다. 음수만큼 포인터를 옮긴다.

반대의 경우, 양수만큼 포인터를 옮긴다.

그렇다면, 이웃한 페이지가 free한지 어떻게 알 수 있을까?

mm/internal.h

찾은 buddy 페이지의 page 포인터를 반환하고, *buddy_pfn에는 주소를 pfn 주소를 대입한다.

그렇다면 buddy pfn 의 주소는 어떻게 찾을까?

free_area 에 tail 추가하는 함수

커널 동적 메모리 할당

페이지를 할당하는것과 가상 메모리를 할당받는것의 차이?
페이지 디스크립터는 할당 받은 페이지의 첫번째 페이지 디스크립터의 주소이고, 그 물리페이지의 속성을 나타내는 메타데이터 구조체이다.

따라서 다음과 같이 코드를 작성해서는 안된다.

void *mem_ptr;
struct page *page_ptr = alloc_pages(gfp_mask, order);
mem_ptr = (void*)page_ptr;
memset(mem_ptr, 0x0, sizeof(order));

페이지를 할당받으면 page_address() 함수를 호출해서 프로세스가 이해할 수 있는 가상 주소로 변환해야한다.

void *mem_ptr;
struct page *page_ptr = alloc_pages(gfp_mask, order);
mem_ptr = (void*)page_address(struct page *page);

Slab memory Allocator

TODO

0개의 댓글