이 코드는 어떤 문제를 푸나요?
초기화 함수는 이름을 순서대로 외우기보다 앞 함수가 뒤 함수에 무엇을 제공하는지 읽으시면 이해하기 쉽습니다. memblock은 초기 RAM 배정을 맡고, buddy는 페이지 단위 할당을 맡으며, SLUB는 작은 객체를 제공합니다. 이 함수는 그 사용 가능 시점을 순서대로 연결합니다.
읽을 범위: v6.6 · mm/mm_init.c · mm_core_init 2762–2800행입니다. 아래에 이 범위의 원문과 각 줄의 설명을 실었습니다. 주제 전체의 흐름과 다른 경로는 기존 분석에서 함께 읽으실 수 있습니다.
먼저 알아둘 개념
초기화 의존 관계
예를 들어 작은 객체 캐시를 만드는 데는 페이지 공급이 필요합니다. 함수 순서를 바꾸면 나중 단계가 아직 준비되지 않은 할당기를 사용할 수 있습니다.
페이지 부가 정보
struct page 외에 디버깅·추적 기능이 요구하는 추가 정보입니다. 연속 공간 요구나 할당기 준비 여부에 따라 초기화 시점이 나뉩니다.
설정에 따른 빈 함수
디버깅 기능이나 아키텍처 기능이 꺼져 있으면 해당 초기화 호출이 빈 함수로 구현될 수 있습니다. 본문에 호출이 있다고 모든 커널에서 같은 메모리를 할당하지는 않습니다.
처음 읽을 때
mem_init → kmem_cache_init → vmalloc_init을 먼저 따라가세요. arm64 v6.6에서 memblock_free_all은 mem_init 내부에 있으므로 함수 호출을 한 단계 더 들어가야 페이지 인계가 보입니다.
더 깊이 살펴볼 때
page_ext의 초기 단계와 buddy·slab 준비 이후 단계를 비교해 보세요. 이 버전의 함수에는 KHO 초기화나 직접적인 memblock_free_all 호출이 없으므로 다른 버전의 순서를 그대로 옮기지 않습니다.
그림으로 보는 변화

1. 초기 할당기로 준비
연속 메타데이터와 디버깅 기반 확보
화살표는 초기화 의존 관계를 나타냅니다. 초기 단계에서는 memblock이 메모리 배정을 지원합니다.
2. 페이지와 객체 할당 준비
mem_init 안에서 페이지 인계 → kmem_cache_init
arm64 v6.6에서는 mem_init 안의 memblock_free_all이 페이지를 넘깁니다. 화살표는 호출 의존 관계입니다.
3. 가상 주소 관리 등 확장
vmalloc_init 및 나머지 메모리 기능 초기화
페이지와 객체 공급이 준비된 뒤 그 위의 기능을 연결합니다. 실제 호출 전체는 아래 원문에 있습니다.
mm_core_init를 한 줄씩 읽기
줄 번호는 v6.6 원문 기준입니다. 주석·빈 줄을 포함한 함수 전체를 먼저 보고, 그 아래에서 각 줄을 설명합니다.
void __init mm_core_init(void)
{
/* Initializations relying on SMP setup */
build_all_zonelists(NULL);
page_alloc_init_cpuhp();
/*
* page_ext requires contiguous pages,
* bigger than MAX_ORDER unless SPARSEMEM.
*/
page_ext_init_flatmem();
mem_debugging_and_hardening_init();
kfence_alloc_pool_and_metadata();
report_meminit();
kmsan_init_shadow();
stack_depot_early_init();
mem_init();
mem_init_print_info();
kmem_cache_init();
/*
* page_owner must be initialized after buddy is ready, and also after
* slab is ready so that stack_depot_init() works properly
*/
page_ext_init_flatmem_late();
kmemleak_init();
ptlock_cache_init();
pgtable_cache_init();
debug_objects_mem_init();
vmalloc_init();
/* If no deferred init page_ext now, as vmap is fully initialized */
if (!deferred_struct_pages)
page_ext_init();
/* Should be run before the first non-init thread is created */
init_espfix_bsp();
/* Should be run after espfix64 is set up. */
pti_init();
kmsan_init_runtime();
mm_cache_init();
}void __init mm_core_init(void)일반 메모리 관리 기능을 부팅 중에 연결하는 함수입니다. __init으로 분류하여 초기화 코드의 수명을 표시합니다.
build_all_zonelists(NULL);할당 시 어느 zone을 어떤 순서로 시도할지 목록을 구성합니다. NULL은 특정 hotplug zone 하나를 추가하는 호출이 아니라 초기 전체 구성을 나타냅니다.
page_alloc_init_cpuhp();CPU가 온라인·오프라인 될 때 페이지 할당기가 처리할 콜백을 준비합니다. CPU별 페이지 캐시의 수명과 연결됩니다.
page_ext_init_flatmem();FLATMEM에서 필요한 초기 페이지 부가 정보를 준비합니다. 이후에는 얻기 어려울 수 있는 큰 연속 메모리 요구를 이 시점에 처리합니다.
mem_debugging_and_hardening_init();페이지에 검사 패턴을 채우는 poisoning, 할당·해제 때 0으로 초기화하는 보호 기능과 debug page allocation 설정을 조정합니다. 서로 함께 쓸 수 없는 요청의 우선순위를 정하고 실제 할당 경로가 사용할 static key를 켜므로 단순한 기능 이름 등록이 아닙니다.
kfence_alloc_pool_and_metadata();KFENCE가 사용할 제한된 객체 풀과 관리 정보를 확보합니다. 표본 기반 메모리 오류 검사를 위한 공간입니다.
report_meminit();스택 자동 초기화 방식과 힙 할당·해제 때 0으로 초기화하는 설정이 이번 부팅에서 켜졌는지 보고합니다. 실제 초기화 동작을 수행하는 호출과 결과를 보여 주는 호출을 구분합니다.
kmsan_init_shadow();KMSAN(Kernel Memory Sanitizer)은 값이 초기화되기 전에 사용되는 오류를 찾습니다. 이 단계는 초기 메모리의 각 값이 정의되었는지 추적할 shadow 메타데이터를 준비하며, 실제 메모리를 모두 0으로 채우는 동작과는 다릅니다.
stack_depot_early_init();여러 객체에서 같은 호출 스택이 반복될 때 스택 주소열을 공유 저장하고 작은 식별자로 참조하는 stack depot을 준비합니다. page_owner나 메모리 오류 진단이 많은 중복 스택을 따로 저장하지 않도록 합니다.
mem_init();아키텍처별 mem_init을 실행합니다. arm64 v6.6에서는 이 경로 안에서 memblock_free_all을 호출하여 사용 가능한 페이지를 buddy에 넘깁니다. 이 함수 바로 앞줄에 인계 호출이 있는 것은 아닙니다.
mem_init_print_info();메모리 초기화 결과와 메모리 사용 현황을 출력합니다. 페이지를 넘기는 동작과 결과를 보고하는 동작을 구분하셔야 합니다.
kmem_cache_init();작은 객체를 캐시별로 할당할 slab 기반을 준비합니다. 이제 페이지를 세분화해 커널 자료구조를 공급할 수 있습니다.
page_ext_init_flatmem_late();buddy와 slab 준비가 필요한 FLATMEM 부가 정보의 나머지 초기화를 수행합니다. page_owner의 스택 추적 같은 요구 때문에 앞 단계와 나뉩니다.
kmemleak_init();kmemleak은 커널의 할당 객체를 추적하고 더 이상 참조되지 않는 것으로 보이는 메모리를 누수 후보로 보고합니다. 이 호출은 초기에 기록한 할당들을 일반 추적 체계에 연결하는 초기화이며 찾은 후보를 자동으로 해제하지 않습니다.
ptlock_cache_init();페이지 테이블별 잠금을 별도 객체로 두는 구성에서 그 잠금 객체를 공급할 캐시를 만듭니다. 서로 다른 테이블을 갱신하는 CPU가 하나의 전역 잠금만으로 경쟁하지 않게 하는 기반이며 구성에 따라 빈 구현일 수 있습니다.
pgtable_cache_init();아키텍처별 페이지 테이블 객체 캐시를 준비합니다. 모든 아키텍처에서 동일한 실체를 갖는 호출은 아닙니다.
debug_objects_mem_init();초기 단계에서 추적하던 디버그 객체 정보를 일반 메모리 관리가 가능한 상태로 전환합니다.
vmalloc_init();커널의 vmalloc/vmap 가상 주소 영역 관리 기반을 초기화합니다. 물리적으로 떨어진 페이지들을 연속 가상 주소로 제공할 준비입니다.
if (!deferred_struct_pages)struct page 초기화를 뒤로 미루지 않는 구성인지 확인합니다. 남은 부가 정보를 지금 초기화할 수 있는지 결정하는 조건입니다.
page_ext_init();지연 초기화를 쓰지 않는 경우 페이지 부가 정보를 준비합니다. 앞줄 조건이 거짓이면 이 호출은 건너뜁니다.
init_espfix_bsp();필요한 아키텍처의 ESPFIX 부트 CPU 준비를 합니다. arm64 설명에서 이 호출을 arm64 고유 기능처럼 읽지 않으셔야 합니다.
pti_init();PTI(Page Table Isolation)는 사용자 실행 때 사용할 페이지 테이블에서 커널 매핑 대부분을 분리하는 완화 기법입니다. 여기서는 이를 사용하는 아키텍처의 후속 테이블 구성을 연결하며 arm64 전용 초기화로 읽으면 안 됩니다.
kmsan_init_runtime();초기화되지 않은 값의 사용을 추적하는 KMSAN의 런타임 상태를 준비합니다. 앞에서 만든 shadow 기반과 이후 실행 중 검사 코드를 연결하는 단계이며 해당 기능을 선택한 빌드에서 의미가 있습니다.
mm_cache_init();프로세스의 사용자 주소 공간을 설명할 mm_struct 객체 캐시를 만듭니다. CPU 마스크와 mm 문맥 식별 정보까지 들어갈 크기를 계산하고, 사용자와 복사할 수 있는 부분은 saved_auxv로 한정합니다. 앞에서 slab 할당기를 준비했으므로 이 캐시를 만들 수 있습니다.
함께 생각해 볼 질문
memblock_free_all 호출이 이 함수에 없는데 페이지는 언제 넘기나요?
arm64 v6.6의 mem_init 내부에서 호출합니다. 공통 함수의 한 호출 아래에서 아키텍처별 작업이 이어지므로 한 단계 더 들어가 확인하셔야 합니다.
kmem_cache_init은 왜 뒤에 있나요?
객체 캐시를 운영할 페이지 공급이 먼저 필요하기 때문입니다. 실제 부트스트랩 세부 과정은 SLUB 초기화 안에서 이어집니다.
페이지 부가 정보 초기화가 여러 번 보이는 이유는 무엇인가요?
각 기능의 메모리 요구와 사용할 수 있는 할당기가 다릅니다. 초기 연속 메모리 단계와 buddy·slab 준비 이후 단계가 구분됩니다.
출처와 읽은 범위
Linux stable v6.6 · mm/mm_init.c
해당 버전 원본 파일 · 기존 코드 분석 · 설명 원고
