# mm_core_init: 부팅용 메모리 관리에서 일반 할당기로 넘어가기

v6.18.37 / mm/mm_init.c

초기화 함수는 이름을 순서대로 외우기보다 앞 함수가 뒤 함수에 무엇을 제공하는지 읽으시면 이해하기 쉽습니다. memblock은 초기 RAM 배정을 맡고, buddy는 페이지 단위 할당을 맡으며, SLUB는 작은 객체를 제공합니다. 이 함수는 그 사용 가능 시점을 순서대로 연결합니다.

## mm_core_init

```c

void __init mm_core_init(void)
{
	arch_mm_preinit();
	hugetlb_bootmem_alloc();

	/* Initializations relying on SMP setup */
	BUILD_BUG_ON(MAX_ZONELISTS > 2);
	build_all_zonelists(NULL);
	page_alloc_init_cpuhp();
	alloc_tag_sec_init();
	/*
	 * page_ext requires contiguous pages,
	 * bigger than MAX_PAGE_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();

	/*
	 * KHO memory setup must happen while memblock is still active, but
	 * as close as possible to buddy initialization
	 */
	kho_memory_init();

	memblock_free_all();
	mem_init();
	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();
	execmem_init();
}

```

### 2679행

```c

void __init mm_core_init(void)

```

일반 메모리 관리 기능을 부팅 중에 연결하는 함수입니다. __init으로 분류하여 초기화 코드의 수명을 표시합니다.

### 2681행

```c

	arch_mm_preinit();

```

아키텍처가 공통 메모리 초기화 전에 해야 하는 준비를 수행합니다. 해당 아키텍처에 특별한 작업이 없으면 빈 구현일 수 있습니다.

### 2682행

```c

	hugetlb_bootmem_alloc();

```

부팅 때 확보해야 하는 HugeTLB 큰 페이지를 준비합니다. 큰 연속 영역은 일반 할당이 진행되어 조각나기 전에 확보하는 것이 유리합니다.

### 2685행

```c

	BUILD_BUG_ON(MAX_ZONELISTS > 2);

```

zonelist 개수가 코드가 예상한 범위를 벗어나면 빌드 단계에서 실패시킵니다. 실행 중 오류를 기다리는 검사가 아닙니다.

### 2686행

```c

	build_all_zonelists(NULL);

```

할당 시 어느 zone을 어떤 순서로 시도할지 목록을 구성합니다. NULL은 특정 hotplug zone 하나를 추가하는 호출이 아니라 초기 전체 구성을 나타냅니다.

### 2687행

```c

	page_alloc_init_cpuhp();

```

CPU가 온라인·오프라인 될 때 페이지 할당기가 처리할 콜백을 준비합니다. CPU별 페이지 캐시의 수명과 연결됩니다.

### 2688행

```c

	alloc_tag_sec_init();

```

할당량을 추적하는 태그 영역을 초기화합니다. 실제 기능 포함 여부는 설정에 따라 달라집니다.

### 2693행

```c

	page_ext_init_flatmem();

```

FLATMEM에서 필요한 초기 페이지 부가 정보를 준비합니다. 이후에는 얻기 어려울 수 있는 큰 연속 메모리 요구를 이 시점에 처리합니다.

### 2694행

```c

	mem_debugging_and_hardening_init();

```

페이지에 검사 패턴을 채우는 poisoning, 할당·해제 때 0으로 초기화하는 보호 기능과 debug page allocation 설정을 조정합니다. 서로 함께 쓸 수 없는 요청의 우선순위를 정하고 실제 할당 경로가 사용할 static key를 켜므로 단순한 기능 이름 등록이 아닙니다.

### 2695행

```c

	kfence_alloc_pool_and_metadata();

```

KFENCE가 사용할 제한된 객체 풀과 관리 정보를 확보합니다. 표본 기반 메모리 오류 검사를 위한 공간입니다.

### 2696행

```c

	report_meminit();

```

스택 자동 초기화 방식과 힙 할당·해제 때 0으로 초기화하는 설정이 이번 부팅에서 켜졌는지 보고합니다. 실제 초기화 동작을 수행하는 호출과 결과를 보여 주는 호출을 구분합니다.

### 2697행

```c

	kmsan_init_shadow();

```

KMSAN(Kernel Memory Sanitizer)은 값이 초기화되기 전에 사용되는 오류를 찾습니다. 이 단계는 초기 메모리의 각 값이 정의되었는지 추적할 shadow 메타데이터를 준비하며, 실제 메모리를 모두 0으로 채우는 동작과는 다릅니다.

### 2698행

```c

	stack_depot_early_init();

```

여러 객체에서 같은 호출 스택이 반복될 때 스택 주소열을 공유 저장하고 작은 식별자로 참조하는 stack depot을 준비합니다. page_owner나 메모리 오류 진단이 많은 중복 스택을 따로 저장하지 않도록 합니다.

### 2704행

```c

	kho_memory_init();

```

kexec handover에서 이어받을 메모리 정보를 처리합니다. memblock을 아직 사용할 수 있으면서 buddy 인계에 가까운 이 시점을 이용합니다.

### 2706행

```c

	memblock_free_all();

```

예약을 제외한 사용 가능 메모리를 일반 페이지 할당기에 넘깁니다. 부팅용 구간 관리에서 페이지 할당으로 넘어가는 중요한 경계입니다.

### 2707행

```c

	mem_init();

```

아키텍처별 메모리 초기화의 마무리를 수행합니다. 앞줄의 페이지 인계와 묶어서 메모리 공급이 준비되는 단계로 보시면 됩니다.

### 2708행

```c

	kmem_cache_init();

```

작은 객체를 캐시별로 할당할 slab 기반을 준비합니다. 이제 페이지를 세분화해 커널 자료구조를 공급할 수 있습니다.

### 2713행

```c

	page_ext_init_flatmem_late();

```

buddy와 slab 준비가 필요한 FLATMEM 부가 정보의 나머지 초기화를 수행합니다. page_owner의 스택 추적 같은 요구 때문에 앞 단계와 나뉩니다.

### 2714행

```c

	kmemleak_init();

```

kmemleak은 커널의 할당 객체를 추적하고 더 이상 참조되지 않는 것으로 보이는 메모리를 누수 후보로 보고합니다. 이 호출은 초기에 기록한 할당들을 일반 추적 체계에 연결하는 초기화이며 찾은 후보를 자동으로 해제하지 않습니다.

### 2715행

```c

	ptlock_cache_init();

```

페이지 테이블별 잠금을 별도 객체로 두는 구성에서 그 잠금 객체를 공급할 캐시를 만듭니다. 서로 다른 테이블을 갱신하는 CPU가 하나의 전역 잠금만으로 경쟁하지 않게 하는 기반이며 구성에 따라 빈 구현일 수 있습니다.

### 2716행

```c

	pgtable_cache_init();

```

아키텍처별 페이지 테이블 객체 캐시를 준비합니다. 모든 아키텍처에서 동일한 실체를 갖는 호출은 아닙니다.

### 2717행

```c

	debug_objects_mem_init();

```

초기 단계에서 추적하던 디버그 객체 정보를 일반 메모리 관리가 가능한 상태로 전환합니다.

### 2718행

```c

	vmalloc_init();

```

커널의 vmalloc/vmap 가상 주소 영역 관리 기반을 초기화합니다. 물리적으로 떨어진 페이지들을 연속 가상 주소로 제공할 준비입니다.

### 2720행

```c

	if (!deferred_struct_pages)

```

struct page 초기화를 뒤로 미루지 않는 구성인지 확인합니다. 남은 부가 정보를 지금 초기화할 수 있는지 결정하는 조건입니다.

### 2721행

```c

		page_ext_init();

```

지연 초기화를 쓰지 않는 경우 페이지 부가 정보를 준비합니다. 앞줄 조건이 거짓이면 이 호출은 건너뜁니다.

### 2723행

```c

	init_espfix_bsp();

```

필요한 아키텍처의 ESPFIX 부트 CPU 준비를 합니다. arm64 설명에서 이 호출을 arm64 고유 기능처럼 읽지 않으셔야 합니다.

### 2725행

```c

	pti_init();

```

PTI(Page Table Isolation)는 사용자 실행 때 사용할 페이지 테이블에서 커널 매핑 대부분을 분리하는 완화 기법입니다. 여기서는 이를 사용하는 아키텍처의 후속 테이블 구성을 연결하며 arm64 전용 초기화로 읽으면 안 됩니다.

### 2726행

```c

	kmsan_init_runtime();

```

초기화되지 않은 값의 사용을 추적하는 KMSAN의 런타임 상태를 준비합니다. 앞에서 만든 shadow 기반과 이후 실행 중 검사 코드를 연결하는 단계이며 해당 기능을 선택한 빌드에서 의미가 있습니다.

### 2727행

```c

	mm_cache_init();

```

프로세스의 사용자 주소 공간을 설명할 mm_struct 객체 캐시를 만듭니다. CPU 마스크와 mm 문맥 식별 정보까지 들어갈 크기를 계산하고, 사용자와 복사할 수 있는 부분은 saved_auxv로 한정합니다. 앞에서 slab 할당기를 준비했으므로 이 캐시를 만들 수 있습니다.

### 2728행

```c

	execmem_init();

```

실행 가능한 커널 메모리 할당 기반을 초기화합니다. 동적으로 생성되는 코드 등을 위한 메모리 관리로 이어집니다.

