# C로 들어온 뒤 커널을 준비하는 순서

v6.18.37 / init/main.c

어셈블리가 C 함수를 실행할 환경을 만들었다고 운영체제 기능이 모두 준비된 것은 아닙니다. start_kernel은 메모리 관리, 스케줄러, 인터럽트와 타이머를 서로 의존하는 순서로 초기화합니다. 각 호출의 이름만 외우기보다 그다음 단계가 무엇을 사용할 수 있게 되는지 보셔야 합니다.

## start_kernel

```c

asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector
void start_kernel(void)
{
	char *command_line;
	char *after_dashes;

	set_task_stack_end_magic(&init_task);
	smp_setup_processor_id();
	debug_objects_early_init();
	init_vmlinux_build_id();

	cgroup_init_early();

	local_irq_disable();
	early_boot_irqs_disabled = true;

	/*
	 * Interrupts are still disabled. Do necessary setups, then
	 * enable them.
	 */
	boot_cpu_init();
	page_address_init();
	pr_notice("%s", linux_banner);
	setup_arch(&command_line);
	/* Static keys and static calls are needed by LSMs */
	jump_label_init();
	static_call_init();
	early_security_init();
	setup_boot_config();
	setup_command_line(command_line);
	setup_nr_cpu_ids();
	setup_per_cpu_areas();
	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */
	early_numa_node_init();
	boot_cpu_hotplug_init();

	pr_notice("Kernel command line: %s\n", saved_command_line);
	/* parameters may set static keys */
	parse_early_param();
	after_dashes = parse_args("Booting kernel",
				  static_command_line, __start___param,
				  __stop___param - __start___param,
				  -1, -1, NULL, &unknown_bootoption);
	print_unknown_bootoptions();
	if (!IS_ERR_OR_NULL(after_dashes))
		parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,
			   NULL, set_init_arg);
	if (extra_init_args)
		parse_args("Setting extra init args", extra_init_args,
			   NULL, 0, -1, -1, NULL, set_init_arg);

	/* Architectural and non-timekeeping rng init, before allocator init */
	random_init_early(command_line);

	/*
	 * These use large bootmem allocations and must precede
	 * initalization of page allocator
	 */
	setup_log_buf(0);
	vfs_caches_init_early();
	sort_main_extable();
	trap_init();
	mm_core_init();
	maple_tree_init();
	poking_init();
	ftrace_init();

	/* trace_printk can be enabled here */
	early_trace_init();

	/*
	 * Set up the scheduler prior starting any interrupts (such as the
	 * timer interrupt). Full topology setup happens at smp_init()
	 * time - but meanwhile we still have a functioning scheduler.
	 */
	sched_init();

	if (WARN(!irqs_disabled(),
		 "Interrupts were enabled *very* early, fixing it\n"))
		local_irq_disable();
	radix_tree_init();

	/*
	 * Set up housekeeping before setting up workqueues to allow the unbound
	 * workqueue to take non-housekeeping into account.
	 */
	housekeeping_init();

	/*
	 * Allow workqueue creation and work item queueing/cancelling
	 * early.  Work item execution depends on kthreads and starts after
	 * workqueue_init().
	 */
	workqueue_init_early();

	rcu_init();
	kvfree_rcu_init();

	/* Trace events are available after this */
	trace_init();

	if (initcall_debug)
		initcall_debug_enable();

	context_tracking_init();
	/* init some links before init_ISA_irqs() */
	early_irq_init();
	init_IRQ();
	tick_init();
	rcu_init_nohz();
	timers_init();
	srcu_init();
	hrtimers_init();
	softirq_init();
	timekeeping_init();
	time_init();

	/* This must be after timekeeping is initialized */
	random_init();

	/* These make use of the fully initialized rng */
	kfence_init();
	boot_init_stack_canary();

	perf_event_init();
	profile_init();
	call_function_init();
	WARN(!irqs_disabled(), "Interrupts were enabled early\n");

	early_boot_irqs_disabled = false;
	local_irq_enable();

	kmem_cache_init_late();

	/*
	 * HACK ALERT! This is early. We're enabling the console before
	 * we've done PCI setups etc, and console_init() must be aware of
	 * this. But we do want output early, in case something goes wrong.
	 */
	console_init();
	if (panic_later)
		panic("Too many boot %s vars at `%s'", panic_later,
		      panic_param);

	lockdep_init();

	/*
	 * Need to run this when irqs are enabled, because it wants
	 * to self-test [hard/soft]-irqs on/off lock inversion bugs
	 * too:
	 */
	locking_selftest();

#ifdef CONFIG_BLK_DEV_INITRD
	if (initrd_start && !initrd_below_start_ok &&
	    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
		pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",
		    page_to_pfn(virt_to_page((void *)initrd_start)),
		    min_low_pfn);
		initrd_start = 0;
	}
#endif
	setup_per_cpu_pageset();
	numa_policy_init();
	acpi_early_init();
	if (late_time_init)
		late_time_init();
	sched_clock_init();
	calibrate_delay();

	arch_cpu_finalize_init();

	pid_idr_init();
	anon_vma_init();
	thread_stack_cache_init();
	cred_init();
	fork_init();
	proc_caches_init();
	uts_ns_init();
	time_ns_init();
	key_init();
	security_init();
	dbg_late_init();
	net_ns_init();
	vfs_caches_init();
	pagecache_init();
	signals_init();
	seq_file_init();
	proc_root_init();
	nsfs_init();
	pidfs_init();
	cpuset_init();
	mem_cgroup_init();
	cgroup_init();
	taskstats_init_early();
	delayacct_init();

	acpi_subsystem_init();
	arch_post_acpi_subsys_init();
	kcsan_init();

	/* Do the rest non-__init'ed, we're now alive */
	rest_init();

	/*
	 * Avoid stack canaries in callers of boot_init_stack_canary for gcc-10
	 * and older.
	 */
#if !__has_attribute(__no_stack_protector__)
	prevent_tail_call_optimization();
#endif
}

```

### 908행

```c

asmlinkage __visible __init __no_sanitize_address __noreturn __no_stack_protector

```

호출 규약·가시성·초기화 섹션·반환 여부 같은 함수 속성을 컴파일러에 알려 줍니다. 이 표기 자체가 초기화 함수를 호출하는 줄은 아닙니다.

### 909행

```c

void start_kernel(void)

```

부트 CPU가 C 초기화 흐름에 들어오는 진입점입니다. 인자와 반환값은 없으며, 메모리·스케줄러·IRQ 등을 준비한 뒤 rest_init으로 넘어가므로 정상적으로 호출자에게 돌아오지 않습니다.

### 911행

```c

	char *command_line;

```

아키텍처 준비 후 돌려받을 부팅 명령줄 주소를 담습니다. 이 선언만으로 문자열 저장소를 할당하거나 내용을 정하지는 않습니다.

### 912행

```c

	char *after_dashes;

```

부팅 명령줄의 -- 뒤에서 시작하는 init 인자 부분의 주소 또는 파싱 오류 표시를 받을 변수입니다.

### 914행

```c

	set_task_stack_end_magic(&init_task);

```

초기 태스크 스택의 끝에 검사 값을 적습니다. 나중에 경계 침범을 감지할 기준을 만드는 단계입니다.

### 915행

```c

	smp_setup_processor_id();

```

부트 CPU의 식별 정보를 초기화하여 뒤의 CPU별 처리가 올바른 대상을 찾게 합니다.

### 916행

```c

	debug_objects_early_init();

```

일반 메모리 할당이 완성되기 전부터 객체 수명 검사에 필요한 초기 상태를 준비합니다.

### 917행

```c

	init_vmlinux_build_id();

```

실행 중인 커널 이미지의 빌드 식별자를 준비하여 진단 결과를 정확한 이미지와 연결합니다.

### 919행

```c

	cgroup_init_early();

```

태스크와 자원 제어 계층의 초기 관계를 준비합니다. 모든 cgroup 기능 초기화가 이 호출 하나로 끝나지는 않습니다.

### 921행

```c

	local_irq_disable();

```

이 CPU의 일반 IRQ 수신을 막습니다. 아직 처리 기반이 불완전한 구간에 IRQ가 끼어들지 않게 합니다.

### 922행

```c

	early_boot_irqs_disabled = true;

```

일반 IRQ를 막은 초기 부팅 단계임을 다른 코드가 알 수 있도록 소프트웨어 표시도 맞춥니다.

### 928행

```c

	boot_cpu_init();

```

부트 CPU를 possible·present·online 등 초기 CPU 집합에 반영합니다.

### 929행

```c

	page_address_init();

```

struct page에서 커널 가상 주소를 찾는 보조 관리 구조가 필요한 구성의 초기화 자리입니다. 해시 방식의 highmem 주소 관리가 필요하지 않은 arm64에서는 빈 매크로가 되며, 실제 RAM이나 페이지 테이블을 할당하는 단계가 아닙니다.

### 930행

```c

	pr_notice("%s", linux_banner);

```

빌드된 커널의 버전과 빌드 식별 정보가 포함된 배너를 출력합니다. 이후 로그를 분석할 때 어떤 커널에서 나온 기록인지 구분할 기준입니다.

### 931행

```c

	setup_arch(&command_line);

```

아키텍처별 부팅 정보를 정리하고 사용할 메모리와 주소 변환, 플랫폼 정보를 준비합니다. 인자로 명령줄 포인터를 돌려받습니다.

### 933행

```c

	jump_label_init();

```

static branch에 필요한 패치 정보를 초기화합니다. 조건마다 일반 분기를 계속 실행하지 않도록 코드를 선택할 기반입니다.

### 934행

```c

	static_call_init();

```

간접 호출을 상황에 맞는 직접 호출 형태로 패치하는 static call 체계를 준비합니다.

### 935행

```c

	early_security_init();

```

아주 초기부터 필요한 보안 모듈의 초기화를 수행합니다. 후반 security_init과 시점을 구분합니다.

### 936행

```c

	setup_boot_config();

```

추가 부팅 설정을 해석하여 뒤의 커널 인자·init 인자 처리에 사용할 정보를 준비합니다.

### 937행

```c

	setup_command_line(command_line);

```

보존용과 파싱용 명령줄을 준비합니다. 파싱 과정에서 원래 부팅 인자 기록을 잃지 않게 합니다.

### 938행

```c

	setup_nr_cpu_ids();

```

시스템이 다룰 CPU 번호 범위를 정리하여 CPU별 배열과 후속 초기화의 기준으로 사용합니다.

### 939행

```c

	setup_per_cpu_areas();

```

각 CPU가 독립적으로 사용할 변수 영역을 준비합니다. 같은 변수 이름이 CPU마다 다른 저장 위치를 갖게 합니다.

### 940행

```c

	smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */

```

부트 CPU가 SMP 체계에서 사용할 아키텍처별 상태를 맞춥니다.

### 941행

```c

	early_numa_node_init();

```

초기 NUMA 노드 상태를 준비하여 CPU와 메모리의 위치 관계를 후속 할당에 연결합니다.

### 942행

```c

	boot_cpu_hotplug_init();

```

CPU 온라인·오프라인 상태 전환 체계에 부트 CPU의 초기 상태를 반영합니다.

### 944행

```c

	pr_notice("Kernel command line: %s\n", saved_command_line);

```

파싱 중 변경될 수 있는 작업용 문자열 대신 보존해 둔 명령줄을 출력합니다. 실제 전달한 부팅 옵션과 이후 적용 결과를 대조할 수 있게 합니다.

### 946행

```c

	parse_early_param();

```

일반 인자 처리보다 먼저 효과가 필요한 early parameter를 해석합니다.

### 947행

```c

	after_dashes = parse_args("Booting kernel",

```

커널 부팅 인자를 파싱하고 -- 뒤의 init 인자 위치를 돌려받기 시작합니다. 등록되지 않은 인자는 아래 unknown_bootoption 처리에 맡깁니다.

### 948행

```c

				  static_command_line, __start___param,

```

수정 가능한 부팅 명령줄과 등록된 커널 인자 테이블의 시작 주소를 넘깁니다.

### 949행

```c

				  __stop___param - __start___param,

```

인자 테이블 끝과 시작의 차이로 등록 항목 수를 계산합니다. 문자열 바이트 길이를 뜻하는 식이 아닙니다.

### 950행

```c

				  -1, -1, NULL, &unknown_bootoption);

```

이 단계의 파라미터 level 범위와 추가 인자, 알 수 없는 옵션의 처리 함수를 전달합니다. unknown_bootoption이 나머지를 init 인자·환경 등으로 분류할 수 있습니다.

### 951행

```c

	print_unknown_bootoptions();

```

해석되지 않은 부팅 옵션을 보고하여 잘못 전달한 설정을 찾을 수 있게 합니다.

### 952행

```c

	if (!IS_ERR_OR_NULL(after_dashes))

```

파싱 결과가 오류 포인터도 NULL도 아닌 경우에만 -- 뒤의 init 인자를 추가 처리합니다.

### 953행

```c

		parse_args("Setting init args", after_dashes, NULL, 0, -1, -1,

```

-- 뒤 문자열을 등록된 커널 파라미터 테이블 없이 다시 파싱합니다. 아래 set_init_arg 콜백으로 사용자 init에 넘길 argv를 구성합니다.

### 954행

```c

			   NULL, set_init_arg);

```

별도 callback 인자 없이 set_init_arg로 각 추가 인자를 init의 argv에 넣도록 합니다.

### 955행

```c

	if (extra_init_args)

```

bootconfig 등에서 추가로 전달할 init 인자 문자열이 준비되어 있는지 확인합니다.

### 956행

```c

		parse_args("Setting extra init args", extra_init_args,

```

추가 init 인자 문자열도 같은 파싱 체계로 처리합니다. 이 호출이 커널 파라미터를 다시 등록하는 것은 아닙니다.

### 957행

```c

			   NULL, 0, -1, -1, NULL, set_init_arg);

```

커널 파라미터 테이블 없이 set_init_arg를 사용하도록 나머지 인자를 전달합니다.

### 960행

```c

	random_init_early(command_line);

```

명령줄 등 초기 정보로 난수 체계의 이른 준비를 합니다. 충분한 엔트로피 확보 완료와는 구분합니다.

### 966행

```c

	setup_log_buf(0);

```

부팅 로그를 보관할 버퍼를 준비합니다. 콘솔 드라이버가 준비되기 전에도 기록을 남길 수 있습니다.

### 967행

```c

	vfs_caches_init_early();

```

이른 경로 탐색에 필요한 VFS 캐시 기반을 준비합니다. 뒤의 전체 VFS 캐시 초기화와 구분합니다.

### 968행

```c

	sort_main_extable();

```

예외 복구 주소를 찾는 테이블을 검색 가능한 순서로 정리합니다.

### 969행

```c

	trap_init();

```

아키텍처가 제공하면 예외 처리 준비 함수를 호출하는 공통 진입점입니다. 이 버전의 arm64는 별도 구현을 제공하지 않아 init/main.c의 빈 __weak 함수가 선택됩니다. 따라서 이 한 줄이 arm64 예외 벡터를 설치한다고 해석해서는 안 됩니다.

### 970행

```c

	mm_core_init();

```

페이지 할당과 커널 메모리 관리의 핵심 초기화를 수행합니다. 후속 서브시스템이 메모리를 사용할 기반입니다.

### 971행

```c

	maple_tree_init();

```

주소 범위 등을 관리할 Maple Tree 자료구조의 초기 상태를 준비합니다.

### 972행

```c

	poking_init();

```

실행 중인 코드의 명령 바이트를 바꾸는 text poking에 별도 준비가 필요한 아키텍처가 사용하는 진입점입니다. 이 버전의 arm64에서는 공통 빈 __weak 구현을 사용하므로 여기서 별도 메모리나 코드 패치가 만들어지지 않습니다.

### 973행

```c

	ftrace_init();

```

함수 추적을 위한 초기 상태와 패치 정보를 준비합니다.

### 976행

```c

	early_trace_init();

```

초기 부팅 경로도 추적할 수 있도록 이른 추적 기반을 준비합니다.

### 983행

```c

	sched_init();

```

런큐와 초기 태스크 관계 등 스케줄러의 자료구조를 준비합니다. 이 호출 즉시 사용자 프로그램을 시작하지는 않습니다.

### 985행

```c

	if (WARN(!irqs_disabled(),

```

이 시점에도 IRQ가 차단되어 있어야 하므로, 너무 일찍 허용되어 있으면 경고하고 아래에서 다시 차단합니다.

### 986행

```c

		 "Interrupts were enabled *very* early, fixing it\n"))

```

IRQ가 이르게 허용된 상태를 알리는 경고 문구입니다. WARN의 조건 결과를 if에서 사용해 복구용 local_irq_disable 실행 여부를 정합니다.

### 987행

```c

		local_irq_disable();

```

이 CPU의 일반 IRQ 수신을 막습니다. 아직 처리 기반이 불완전한 구간에 IRQ가 끼어들지 않게 합니다.

### 988행

```c

	radix_tree_init();

```

인덱스로 객체를 찾는 공통 자료구조의 초기 기반을 준비합니다.

### 994행

```c

	housekeeping_init();

```

주기 작업 등을 담당할 CPU 집합의 정책을 준비하여 격리 CPU 설정과 후속 작업 배치를 연결합니다.

### 1001행

```c

	workqueue_init_early();

```

작업 큐를 등록할 초기 상태를 준비합니다. 실제 worker 실행 기반이 완성되는 시점은 뒤의 초기화와 구분합니다.

### 1003행

```c

	rcu_init();

```

독자 종료와 콜백 처리를 추적할 RCU 상태를 준비합니다.

### 1004행

```c

	kvfree_rcu_init();

```

RCU 이후 메모리 회수를 처리할 별도의 초기 상태를 준비합니다.

### 1007행

```c

	trace_init();

```

추적 기능의 후속 초기화를 수행합니다. 초기 추적 단계가 요구하는 기반과 구분합니다.

### 1009행

```c

	if (initcall_debug)

```

initcall_debug 부팅 옵션으로 각 초기화 함수의 실행 추적을 요청했는지 확인합니다. 참이면 다음 호출에서 initcall 실행 전후 진단을 연결합니다.

### 1010행

```c

		initcall_debug_enable();

```

initcall 진단 요청이 있을 때 각 초기화 함수의 실행을 추적할 처리를 켭니다.

### 1012행

```c

	context_tracking_init();

```

사용자·커널·idle 실행 문맥 변화를 추적할 초기 상태를 준비합니다.

### 1014행

```c

	early_irq_init();

```

Linux IRQ 번호를 관리할 소프트웨어 객체를 먼저 만듭니다.

### 1015행

```c

	init_IRQ();

```

아키텍처별 IRQ 스택과 인터럽트 컨트롤러 초기화 경로를 연결합니다.

### 1016행

```c

	tick_init();

```

주기 tick의 기본 상태를 준비하여 스케줄러와 시간 관리에 연결합니다.

### 1017행

```c

	rcu_init_nohz();

```

주기 tick이 생략되는 CPU 동작을 RCU 추적과 연결하는 초기화를 수행합니다.

### 1018행

```c

	timers_init();

```

일반 커널 타이머의 자료구조를 초기화합니다.

### 1019행

```c

	srcu_init();

```

잠들 수 있는 읽기 구간을 지원하는 SRCU 관련 초기화를 수행합니다.

### 1020행

```c

	hrtimers_init();

```

고해상도 타이머 큐와 CPU별 기반을 준비합니다.

### 1021행

```c

	softirq_init();

```

지연 처리를 실행할 softirq 항목과 초기 상태를 준비합니다.

### 1022행

```c

	timekeeping_init();

```

시계 원천을 시간 값으로 누적하는 커널 시간 관리 상태를 준비합니다.

### 1023행

```c

	time_init();

```

arm64에서는 장치 트리의 클록과 타이머 드라이버를 찾고 hrtimer broadcast를 준비합니다. 아키텍처 타이머의 주파수를 확인해 짧은 지연 계산의 기준도 정하며, 주파수가 0이면 부팅을 중단합니다. 앞의 timekeeping 상태 준비에 실제 장치 기반을 연결하는 순서입니다.

### 1026행

```c

	random_init();

```

난수 체계의 후속 초기화를 수행합니다. 이후 보안 키나 스택 보호 값 생성과 연결됩니다.

### 1029행

```c

	kfence_init();

```

KFENCE는 일부 힙 할당을 표본으로 골라 보호 페이지 등을 이용해 범위 초과·해제 후 사용·잘못된 해제를 잡는 검사기입니다. 이 호출은 앞에서 확보한 풀을 실제 검사에 사용할 상태로 연결하며, 모든 메모리 접근을 검사하는 기능은 아닙니다.

### 1030행

```c

	boot_init_stack_canary();

```

부트 태스크가 사용할 스택 훼손 검사 값을 준비합니다.

### 1032행

```c

	perf_event_init();

```

성능 계측 이벤트를 관리할 공통 기반을 준비합니다.

### 1033행

```c

	profile_init();

```

설정된 프로파일링 기능의 초기 상태를 준비합니다.

### 1034행

```c

	call_function_init();

```

다른 CPU에 함수 실행을 요청하는 SMP 호출 기반을 준비합니다.

### 1035행

```c

	WARN(!irqs_disabled(), "Interrupts were enabled early\n");

```

정식 IRQ 활성화 직전에 여전히 차단 상태인지 진단합니다. 앞의 if(WARN(...))와 달리 이 줄에는 별도의 조건부 재차단 본문이 없습니다.

### 1037행

```c

	early_boot_irqs_disabled = false;

```

이른 IRQ 차단 단계가 끝났다는 소프트웨어 표시를 먼저 바꾸고 다음 줄에서 실제 IRQ를 허용합니다.

### 1038행

```c

	local_irq_enable();

```

필요한 IRQ 기반을 준비한 뒤 이 CPU의 일반 IRQ 수신을 허용합니다.

### 1040행

```c

	kmem_cache_init_late();

```

인터럽트와 메모리 기반이 갖춰진 뒤 슬랩 할당기의 후반 초기화를 수행합니다.

### 1047행

```c

	console_init();

```

콘솔을 준비하여 기존 부팅 로그와 이후 메시지를 사용자에게 보여 줄 수 있게 합니다.

### 1048행

```c

	if (panic_later)

```

부팅 인자 처리 중 발견했지만 출력 기반이 준비될 때까지 미룬 치명적 오류가 있는지 확인합니다. console_init 뒤이므로 이유를 보여 주고 중단할 수 있습니다.

### 1049행

```c

		panic("Too many boot %s vars at `%s'", panic_later,

```

계속 진행할 수 없는 부팅 인자 오류를 보고하고 커널 실행을 중단합니다.

### 1050행

```c

		      panic_param);

```

앞의 panic 메시지에 원인이 된 부팅 인자 문자열을 넘깁니다. 어떤 옵션 때문에 인자나 환경 배열의 한도를 넘었는지 진단할 수 있게 합니다.

### 1052행

```c

	lockdep_init();

```

잠금 의존성 검사기의 한도와 메모리 사용 정보를 보고합니다. 이름만 보고 여기서 모든 검사가 수행된다고 해석하지 않습니다.

### 1059행

```c

	locking_selftest();

```

잠금 검사 기능이 예상한 잘못된 패턴을 감지하는지 자가 검사를 수행합니다.

### 1061행

```c

#ifdef CONFIG_BLK_DEV_INITRD

```

initramfs/initrd를 초기 루트 준비에 사용할 수 있게 하는 설정입니다. 켜진 빌드에서는 아래에서 initrd 위치가 허용된 메모리 범위인지 검사하고 잘못된 영역을 사용하지 않게 합니다. 꺼져 있으면 이 검사 블록이 포함되지 않습니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1062행

```c

	if (initrd_start && !initrd_below_start_ok &&

```

initrd가 있고 낮은 주소에 놓이는 예외도 허용되지 않는 경우에만 다음 줄의 위치 검사를 이어 갑니다.

### 1063행

```c

	    page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {

```

initrd 시작을 페이지 번호로 바꾸어 유효한 저위 메모리 시작보다 낮은지 비교합니다. 앞줄 if의 마지막 조건이며 독립적인 함수 호출문이 아닙니다.

### 1064행

```c

		pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n",

```

initrd가 허용된 저위 메모리 시작보다 아래에 있어 사용할 수 없음을 기록합니다. 다음 줄들에서 실제 페이지 번호와 기준을 출력하고 initrd_start를 지워 사용을 막습니다.

### 1065행

```c

		    page_to_pfn(virt_to_page((void *)initrd_start)),

```

로그에 initrd 시작의 페이지 번호를 넣습니다. virt_to_page로 페이지 정보를 얻고 page_to_pfn으로 번호를 구하므로 출력값은 바이트 주소와 단위가 다릅니다. 앞줄에서 시작한 호출이나 식에 이어지는 부분이므로, 이 줄만으로 별도의 함수 호출이 생기지는 않습니다.

### 1066행

```c

		    min_low_pfn);

```

비교 기준인 사용 가능한 저위 메모리의 시작 페이지 번호를 출력에 넘깁니다. 앞의 initrd 페이지 번호가 이 값보다 작아 경고한 이유를 보여 줍니다.

### 1067행

```c

		initrd_start = 0;

```

사용할 수 없다고 판단한 initrd의 시작 표시를 지워 이후 경로가 이를 사용하지 않게 합니다. initrd 데이터 전체를 0으로 덮어쓰는 줄이 아닙니다.

### 1069행

```c

#endif

```

initramfs/initrd 지원 시에만 포함하는 초기 이미지 위치 검사를 마칩니다. 이후 페이지 할당기와 NUMA 정책 초기화는 이 조건 바깥에서 이어집니다.

### 1070행

```c

	setup_per_cpu_pageset();

```

페이지 할당을 빠르게 처리할 CPU별 페이지 저장소를 준비합니다.

### 1071행

```c

	numa_policy_init();

```

메모리 노드 선택 정책의 기본값과 관리 상태를 준비합니다.

### 1072행

```c

	acpi_early_init();

```

이른 ACPI 해석과 기본 상태를 준비합니다. 후속 서브시스템 초기화와 시점을 나눕니다.

### 1073행

```c

	if (late_time_init)

```

아키텍처가 지연 실행해야 할 시간 초기화 함수를 포인터에 등록했는지 확인합니다. 등록하지 않은 플랫폼은 다음 호출을 건너뜁니다.

### 1074행

```c

		late_time_init();

```

플랫폼이 등록한 후반 시간 초기화 함수가 있을 때 호출합니다.

### 1075행

```c

	sched_clock_init();

```

스케줄링 시간 측정에 쓰는 시계의 기반을 맞춥니다.

### 1076행

```c

	calibrate_delay();

```

짧은 바쁜 대기 루프가 필요한 시간을 근사하도록 보정합니다.

### 1078행

```c

	arch_cpu_finalize_init();

```

아키텍처별 CPU 기능 초기화를 마무리합니다.

### 1080행

```c

	pid_idr_init();

```

태스크를 PID로 찾고 관리하는 초기 상태를 준비합니다.

### 1081행

```c

	anon_vma_init();

```

익명 메모리 매핑 사이 관계를 관리할 객체 저장소를 준비합니다.

### 1082행

```c

	thread_stack_cache_init();

```

태스크 커널 스택을 할당·회수할 저장소를 준비합니다.

### 1083행

```c

	cred_init();

```

자격·권한 정보를 보관하는 객체 관리 기반을 준비합니다.

### 1084행

```c

	fork_init();

```

task_struct 캐시와 태스크 생성 한도를 준비합니다.

### 1085행

```c

	proc_caches_init();

```

프로세스 자원 관리에 쓰이는 공통 객체 캐시들을 준비합니다.

### 1086행

```c

	uts_ns_init();

```

호스트 이름 등 UTS namespace의 초기 상태를 준비합니다.

### 1087행

```c

	time_ns_init();

```

namespace별 시간 표현을 위한 초기 상태를 준비합니다.

### 1088행

```c

	key_init();

```

커널 키 관리 체계의 저장소와 상태를 초기화합니다.

### 1089행

```c

	security_init();

```

보안 모듈의 일반 초기화를 수행합니다. 이른 보안 초기화와 분리된 단계입니다.

### 1090행

```c

	dbg_late_init();

```

일반 실행 기반이 준비된 뒤 디버그 기능의 후반 초기화를 수행합니다.

### 1091행

```c

	net_ns_init();

```

초기 네트워크 namespace와 관련 관리 기반을 준비합니다.

### 1092행

```c

	vfs_caches_init();

```

이름·dentry·inode·열린 파일 등의 VFS 객체 저장소를 준비합니다.

### 1093행

```c

	pagecache_init();

```

페이지 캐시 관리에서 요구하는 초기 정책과 상태를 준비합니다.

### 1094행

```c

	signals_init();

```

시그널 정보 객체를 관리할 초기 저장소를 준비합니다.

### 1095행

```c

	seq_file_init();

```

여러 줄의 커널 상태를 파일로 읽게 하는 seq_file 기반을 준비합니다.

### 1096행

```c

	proc_root_init();

```

proc 파일 시스템의 루트와 기본 항목을 준비합니다.

### 1097행

```c

	nsfs_init();

```

namespace 객체를 파일로 나타내는 기반을 준비합니다.

### 1098행

```c

	pidfs_init();

```

PID 객체와 연결된 파일 기반 관리 체계를 초기화합니다.

### 1099행

```c

	cpuset_init();

```

태스크가 사용할 CPU·메모리 노드 집합의 관리 기반을 준비합니다.

### 1100행

```c

	mem_cgroup_init();

```

메모리 자원 사용량을 cgroup별로 관리할 초기화를 수행합니다.

### 1101행

```c

	cgroup_init();

```

초기 cgroup 준비 이후 필요한 일반 관리 구조를 완성합니다.

### 1102행

```c

	taskstats_init_early();

```

태스크 사용량 통계를 수집할 초기 상태를 준비합니다.

### 1103행

```c

	delayacct_init();

```

태스크가 여러 자원을 기다린 시간을 기록할 기반을 준비합니다.

### 1105행

```c

	acpi_subsystem_init();

```

ACPI 서브시스템의 후속 초기화를 수행합니다.

### 1106행

```c

	arch_post_acpi_subsys_init();

```

ACPI 서브시스템 준비 후 필요한 아키텍처별 작업을 수행합니다.

### 1107행

```c

	kcsan_init();

```

KCSAN(Kernel Concurrency Sanitizer)은 동기화가 부족한 동시 메모리 접근으로 생기는 데이터 경쟁을 표본 기반으로 찾습니다. 부팅 중 검사 상태를 초기화하는 호출이며 경쟁을 자동으로 고치거나 잠금을 추가하지는 않습니다.

### 1110행

```c

	rest_init();

```

단일 부팅 흐름을 init·kthreadd·idle의 실행으로 나눕니다.

### 1116행

```c

#if !__has_attribute(__no_stack_protector__)

```

컴파일러가 함수별 스택 보호 제외 속성을 지원하지 않는 경우의 보완 코드입니다. rest_init 호출 뒤 형태를 남겨 꼬리 호출 최적화가 부팅 중 스택 canary 처리와 어긋나는 코드를 만들지 않도록 합니다. 이 선택은 전처리 단계에서 이루어지며, CPU가 실행 중 이 줄의 조건을 검사하지 않습니다.

### 1117행

```c

	prevent_tail_call_optimization();

```

특정 컴파일러 구성에서 꼬리 호출 최적화가 호출 프레임을 없애지 않도록 합니다. 기능 실행을 추가하기보다 생성 코드 형태를 제어하는 목적입니다.

### 1118행

```c

#endif

```

스택 보호 제외 속성이 없는 컴파일러에만 필요한 꼬리 호출 방지 구간을 마칩니다. 정상 부팅은 앞의 rest_init 또는 그 진입 래퍼에서 계속되어 이 끝으로 반환하지 않습니다.

