# 초기화용 메모리를 돌려주기 전의 준비

v6.18.37 / init/main.c

init 태스크가 시스템을 사용자 공간으로 넘기기 전에 CPU와 드라이버, 루트 파일 시스템을 준비합니다. 함수 이름의 freeable은 이 단계의 초기화 코드를 나중에 회수할 수 있다는 뜻입니다. initramfs 준비처럼 다른 작업이 끝나기를 기다리는 단계도 포함합니다.

## kernel_init_freeable

```c

static noinline void __init kernel_init_freeable(void)
{
	/* Now the scheduler is fully set up and can do blocking allocations */
	gfp_allowed_mask = __GFP_BITS_MASK;

	/*
	 * init can allocate pages on any node
	 */
	set_mems_allowed(node_states[N_MEMORY]);

	cad_pid = get_pid(task_pid(current));

	smp_prepare_cpus(setup_max_cpus);

	workqueue_init();

	init_mm_internals();

	do_pre_smp_initcalls();
	lockup_detector_init();

	smp_init();
	sched_init_smp();

	workqueue_init_topology();
	async_init();
	padata_init();
	page_alloc_init_late();

	do_basic_setup();

	kunit_run_all_tests();

	wait_for_initramfs();
	console_on_rootfs();

	/*
	 * check if there is an early userspace init.  If yes, let it do all
	 * the work
	 */
	int ramdisk_command_access;
	ramdisk_command_access = init_eaccess(ramdisk_execute_command);
	if (ramdisk_command_access != 0) {
		pr_warn("check access for rdinit=%s failed: %i, ignoring\n",
			ramdisk_execute_command, ramdisk_command_access);
		ramdisk_execute_command = NULL;
		prepare_namespace();
	}

	/*
	 * Ok, we have completed the initial bootup, and
	 * we're essentially up and running. Get rid of the
	 * initmem segments and start the user-mode stuff..
	 *
	 * rootfs is available now, try loading the public keys
	 * and default modules
	 */

	integrity_load_keys();
}

```

### 1563행

```c

static noinline void __init kernel_init_freeable(void)

```

init 태스크에서 SMP와 장치·초기 루트 파일 시스템 준비를 진행합니다. __init으로 묶인 이 코드의 메모리는 호출 즉시가 아니라 초기화 완료 뒤 별도 단계에서 회수할 수 있습니다.

### 1566행

```c

	gfp_allowed_mask = __GFP_BITS_MASK;

```

이후 할당에서 사용할 수 있는 GFP 동작의 제한을 풀어 정상 할당 정책을 허용합니다.

### 1571행

```c

	set_mems_allowed(node_states[N_MEMORY]);

```

init 태스크가 사용할 수 있는 메모리 노드 집합을 넓힙니다.

### 1573행

```c

	cad_pid = get_pid(task_pid(current));

```

PID 객체의 참조를 얻어 이후에도 유효한 대상으로 유지합니다.

### 1575행

```c

	smp_prepare_cpus(setup_max_cpus);

```

사용할 CPU 개수에 맞춰 보조 CPU의 초기화 준비를 수행합니다.

### 1577행

```c

	workqueue_init();

```

실제 작업을 실행할 worker 기반을 준비합니다.

### 1579행

```c

	init_mm_internals();

```

메모리 관리의 CPU별 작업 큐를 만들고, SMP에서는 CPU가 추가·제거될 때 메모리 통계를 정리할 콜백과 주기 갱신을 연결합니다. procfs를 쓰면 buddyinfo·vmstat·zoneinfo 같은 조회 파일도 등록합니다. 앞의 workqueue_init 뒤에 실행되어 실제 작업을 예약할 기반을 갖춥니다. v6.18.37에서는 vm sysctl 항목도 여기서 등록합니다.

### 1581행

```c

	do_pre_smp_initcalls();

```

보조 CPU를 시작하기 전에 실행해야 하는 초기화 함수들을 호출합니다.

### 1582행

```c

	lockup_detector_init();

```

CPU가 오랫동안 진행하지 못하는 상황을 감지할 기반을 준비합니다.

### 1584행

```c

	smp_init();

```

보조 CPU들을 온라인으로 올리는 SMP 초기화를 수행합니다.

### 1585행

```c

	sched_init_smp();

```

여러 CPU 사이 스케줄링 관계를 구성합니다.

### 1587행

```c

	workqueue_init_topology();

```

CPU 토폴로지에 맞춰 작업 큐의 실행 배치를 정리합니다.

### 1588행

```c

	async_init();

```

비동기 초기화 작업 관리 기반을 준비합니다.

### 1589행

```c

	padata_init();

```

순서를 유지하며 병렬 작업을 처리하는 padata 기반을 준비합니다.

### 1590행

```c

	page_alloc_init_late();

```

CPU·일반 할당 기반이 갖춰진 뒤 페이지 할당기의 후반 초기화를 수행합니다.

### 1592행

```c

	do_basic_setup();

```

드라이버 등 기본 서브시스템의 initcall 초기화를 실행합니다.

### 1594행

```c

	kunit_run_all_tests();

```

구성된 KUnit 테스트를 실행합니다. 모든 시스템에서 항상 같은 테스트가 포함되지는 않습니다.

### 1596행

```c

	wait_for_initramfs();

```

비동기로 풀고 있는 initramfs 작업이 끝나기를 기다립니다.

### 1597행

```c

	console_on_rootfs();

```

초기 루트 파일 시스템의 콘솔을 준비합니다.

### 1603행

```c

	int ramdisk_command_access;

```

ramdisk init 프로그램에 접근할 수 있는지 확인한 결과를 보관합니다. 0은 성공이며 실패한 경우 같은 오류 코드를 경고에 출력하기 위해 별도 변수에 둡니다.

### 1604행

```c

	ramdisk_command_access = init_eaccess(ramdisk_execute_command);

```

지정 init 프로그램에 접근할 수 있는지 확인하고 결과를 기록합니다.

### 1605행

```c

	if (ramdisk_command_access != 0) {

```

ramdisk init 접근 검사가 실패한 경우입니다. 해당 실행 경로를 포기하고 prepare_namespace로 일반 루트 파일 시스템 준비를 이어 갑니다.

### 1606행

```c

		pr_warn("check access for rdinit=%s failed: %i, ignoring\n",

```

사용하려던 ramdisk init 경로와 접근 실패 이유를 경고합니다. 단순 출력 뒤에 끝내지 않고 아래에서 경로를 NULL로 지워 사용하지 않게 합니다.

### 1607행

```c

			ramdisk_execute_command, ramdisk_command_access);

```

진단 문구에 실제 실행 파일 경로와 접근 검사 오류 코드를 넣습니다. 나중에 경로 자체가 잘못된 것인지 권한 등 다른 원인인지 확인할 단서입니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.

### 1608행

```c

		ramdisk_execute_command = NULL;

```

접근할 수 없었던 ramdisk init 경로를 사용하지 않도록 포인터를 지웁니다. 다음 prepare_namespace가 후속 루트 준비를 맡습니다.

### 1609행

```c

		prepare_namespace();

```

ramdisk init 경로를 사용하지 못한 경우 루트 파일 시스템 등 후속 실행 환경을 준비합니다.

### 1621행

```c

	integrity_load_keys();

```

사용 가능한 초기 파일 시스템에서 무결성 검증 키를 읽는 준비를 수행합니다.

