# init·kthreadd와 부트 CPU의 역할을 나눕니다

v6.18.37 / init/main.c

한 CPU에서 이어지던 초기화 흐름을 이제 여러 태스크의 실행으로 나눕니다. 사용자 init으로 이어질 태스크와 커널 스레드를 만드는 kthreadd를 생성하고, 원래 부트 태스크는 idle 경로로 들어갑니다. 생성과 실행은 같은 순간이 아니므로 준비 완료 통지도 필요합니다.

## rest_init

```c

static noinline void __ref __noreturn rest_init(void)
{
	struct task_struct *tsk;
	int pid;

	rcu_scheduler_starting();
	/*
	 * We need to spawn init first so that it obtains pid 1, however
	 * the init task will end up wanting to create kthreads, which, if
	 * we schedule it before we create kthreadd, will OOPS.
	 */
	pid = user_mode_thread(kernel_init, NULL, CLONE_FS);
	/*
	 * Pin init on the boot CPU. Task migration is not properly working
	 * until sched_init_smp() has been run. It will set the allowed
	 * CPUs for init to the non isolated CPUs.
	 */
	rcu_read_lock();
	tsk = find_task_by_pid_ns(pid, &init_pid_ns);
	tsk->flags |= PF_NO_SETAFFINITY;
	set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));
	rcu_read_unlock();

	numa_default_policy();
	pid = kernel_thread(kthreadd, NULL, NULL, CLONE_FS | CLONE_FILES);
	rcu_read_lock();
	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
	rcu_read_unlock();

	/*
	 * Enable might_sleep() and smp_processor_id() checks.
	 * They cannot be enabled earlier because with CONFIG_PREEMPTION=y
	 * kernel_thread() would trigger might_sleep() splats. With
	 * CONFIG_PREEMPT_VOLUNTARY=y the init task might have scheduled
	 * already, but it's stuck on the kthreadd_done completion.
	 */
	system_state = SYSTEM_SCHEDULING;

	complete(&kthreadd_done);

	/*
	 * The boot idle thread must execute schedule()
	 * at least once to get things moving:
	 */
	schedule_preempt_disabled();
	/* Call into cpu_idle with preempt disabled */
	cpu_startup_entry(CPUHP_ONLINE);
}

```

### 711행

```c

static noinline void __ref __noreturn rest_init(void)

```

초기화 흐름을 init, kthreadd와 부트 CPU의 idle 실행으로 나눕니다. noinline은 별도 함수 형태를 유지하고 __ref는 참조가 필요한 코드 구역에 두며 __noreturn은 정상 반환하지 않음을 표시합니다.

### 713행

```c

	struct task_struct *tsk;

```

생성된 init 태스크를 PID로 찾아 플래그와 CPU 허용 범위를 바꿀 때 사용할 task_struct 포인터입니다. 포인터가 보관하는 것은 객체의 주소입니다. 이 선언만으로 대상 구조체나 문자열의 내용이 복사되지는 않습니다.

### 714행

```c

	int pid;

```

태스크 생성 함수가 반환한 PID 번호를 담습니다. 아래에서 이 번호를 task_struct 포인터로 다시 조회하므로 두 종류의 값을 구분합니다.

### 716행

```c

	rcu_scheduler_starting();

```

스케줄러 시작 단계로 RCU 상태를 전환하여 후속 태스크 활동을 추적하게 합니다.

### 722행

```c

	pid = user_mode_thread(kernel_init, NULL, CLONE_FS);

```

kernel_init부터 실행할 init 태스크를 만들고 PID를 받습니다. CLONE_FS는 현재 디렉터리·루트·umask를 담은 파일 시스템 문맥을 부모와 공유하라는 뜻입니다. 여기서 즉시 사용자 명령을 실행하는 것이 아니라 kernel_init의 준비를 거친 뒤 exec로 사용자 init을 실행합니다.

### 728행

```c

	rcu_read_lock();

```

참조하는 RCU 보호 객체의 수명이 읽기 도중 끝나지 않도록 읽기 구간을 시작합니다.

### 729행

```c

	tsk = find_task_by_pid_ns(pid, &init_pid_ns);

```

PID 숫자를 해당 namespace의 task_struct 포인터로 바꿔 찾습니다.

### 730행

```c

	tsk->flags |= PF_NO_SETAFFINITY;

```

후속 준비 전에 다른 코드가 init 태스크의 CPU 배치를 바꾸지 못하게 플래그를 추가합니다.

### 731행

```c

	set_cpus_allowed_ptr(tsk, cpumask_of(smp_processor_id()));

```

초기 init 태스크를 부트 CPU에 묶어 아직 준비 중인 다른 CPU에서 실행되지 않게 합니다.

### 732행

```c

	rcu_read_unlock();

```

RCU 보호 읽기 구간을 끝냅니다. 잠든 독자를 기다리는 일반 mutex unlock과는 다릅니다.

### 734행

```c

	numa_default_policy();

```

기본 메모리 노드 배치 정책을 준비합니다.

### 735행

```c

	pid = kernel_thread(kthreadd, NULL, NULL, CLONE_FS | CLONE_FILES);

```

커널 스레드 생성 요청을 처리할 kthreadd를 만듭니다. CLONE_FS는 루트·현재 디렉터리 등을, CLONE_FILES는 열린 파일 디스크립터 표를 부모와 공유하게 합니다. 앞에서 만든 init은 나중에 사용자 프로그램을 실행하지만 kthreadd는 커널 스레드로 남습니다.

### 736행

```c

	rcu_read_lock();

```

참조하는 RCU 보호 객체의 수명이 읽기 도중 끝나지 않도록 읽기 구간을 시작합니다.

### 737행

```c

	kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);

```

PID 숫자를 해당 namespace의 task_struct 포인터로 바꿔 찾습니다.

### 738행

```c

	rcu_read_unlock();

```

RCU 보호 읽기 구간을 끝냅니다. 잠든 독자를 기다리는 일반 mutex unlock과는 다릅니다.

### 747행

```c

	system_state = SYSTEM_SCHEDULING;

```

시스템이 초기 스케줄링 단계에 들어갔음을 전역 상태에 기록합니다.

### 749행

```c

	complete(&kthreadd_done);

```

kthreadd가 준비되었음을 알립니다. 기다리는 init 경로가 후속 작업을 시작할 수 있습니다.

### 755행

```c

	schedule_preempt_disabled();

```

선점이 비활성인 조건에서 첫 스케줄링 전환을 수행합니다.

### 757행

```c

	cpu_startup_entry(CPUHP_ONLINE);

```

원래 부트 태스크를 CPU idle 실행 경로로 보냅니다.

