운영체제 기본/x86-64 Linux의 BIOS 부팅/PID 1 생성: 두 판 사이의 차이
BIOS GRUB 부팅 경로 보강과 각론 형식 통일 |
잔글 관리자님이 운영체제 기본/x86-64 Linux의 부팅/PID 1 생성 문서를 운영체제 기본/x86-64 Linux의 BIOS 부팅/PID 1 생성 문서로 이동했습니다: x86-64 전용 각론의 아키텍처와 장치·부팅 범위를 제목에 명시 |
(차이 없음)
| |
2026년 10월 31일 (토) 09:00 판
Linux v6.12에서 부트 태스크는 rest_init()에 도달하면 PID 1과 kthreadd를 만든다. PID 1은 커널의 나머지 초기화를 맡고, 기존 부트 태스크인 PID 0은 idle 태스크로 남는다.
PID 0이 PID 1로 바뀌는 것은 아니다
rest_init()은 먼저 user_mode_thread(kernel_init, NULL, CLONE_FS)를 호출해 PID 1을 만든다. 이어 kernel_thread()로 kthreadd(PID 2)를 만든다. 기존 부트 태스크는 스케줄러를 거쳐 cpu_startup_entry()의 idle 경로로 진행한다.[1]
즉 rest_init()이 자기 스택에서 평범하게 kernel_init()을 호출하고 그대로 init이 되는 구조가 아니다. 새 태스크에는 별도 실행 문맥이 생긴다. 실행 상태와 자원의 구분은 프로세스와 스레드에서 설명한다. 이후 어떤 태스크가 CPU를 사용할지는 스케줄링에 따라 달라진다. PID 1은 새로 생겼지만 기존 PID 0이 사라진 것은 아니다.[2]
PID 0: start_kernel -> rest_init -----------------> idle
|
+-- create PID 1 -> kernel_init
|
+-- create PID 2 -> kthreadd
user_mode_thread인데 왜 커널 함수를 실행하는가
함수 이름의 user mode를 “이 호출 직후 CPU가 사용자 모드가 된다”로 읽으면 안 된다. user_mode_thread()는 시작 함수와 인자를 kernel_clone_args에 담아 kernel_clone()에 넘긴다. 이 경우 시작 함수는 커널 내부 함수인 kernel_init()이다.[2]
kernel_thread()와의 중요한 차이는 kthread 속성을 설정하지 않는다는 점이다. 계속 커널 스레드로 남을 태스크와 나중에 사용자 실행 이미지를 갖게 될 태스크의 생성 방식이 구별된다. 실제로 kernel_execve()는 PF_KTHREAD가 설정된 태스크의 호출을 거부한다. 따라서 “PID 1은 처음에 커널 코드를 실행한다”는 설명은 맞지만, 이를 곧바로 “일반 커널 스레드를 만든 뒤 exec한다”로 바꾸면 v6.12 구현과 어긋난다.[2][3]
권한 수준과 태스크 종류도 같은 말이 아니다. 사용자 프로그램을 실행할 태스크 역시 커널에 진입해 커널 코드를 실행할 수 있다. PID 1은 아직 첫 사용자 실행 이미지를 얻기 전이다. 보호 경계 자체는 보호를 참고한다.
먼저 만든 PID 1을 왜 기다리게 하는가
PID 1이라는 번호를 얻도록 init을 먼저 생성하지만, init이 진행하며 사용할 커널 스레드 생성 기반인 kthreadd는 그다음 만들어진다. 단순히 소스에서 생성 호출을 두 줄 연속 적었다고 새 태스크들이 반드시 원하는 순서로 실행되는 것은 아니다.[1]
이 의존 관계를 표현하는 것이 kthreadd_done completion이다. completion은 한쪽이 작업 완료를 알리고 다른 쪽이 그 사건을 기다리는 동기화 수단이다. 조건을 기다리는 작업과 깨우기의 관계는 동시성과 연결된다. kernel_init()은 시작하자마자 기다리고, rest_init()은 kthreadd 준비 후 완료를 알린다. “태스크를 먼저 만들었다”와 “필요한 기반이 준비되었으니 진행해도 된다”를 분리한 것이다.[1]
PID 1에서 커널 초기화를 계속한다
대기가 끝나면 kernel_init_freeable()이 SMP와 나머지 초기화를 진행한다. 여기에는 do_basic_setup()을 통한 initcall 실행도 포함된다.[1]
initcall은 부팅 중 실행할 초기화 함수를 정해진 단계에 등록하는 방식이다. 소스의 각 부분이 등록한 함수들을 초기화 경로가 순회하며 실행하므로, start_kernel() 안에 모든 드라이버의 함수 이름이 직접 나열될 필요는 없다. 예를 들어 rootfs에 initramfs를 푸는 populate_rootfs()도 rootfs_initcall로 등록되어 있다.[1][4]
kernel_init_freeable()에서 돌아온 뒤에도 비동기 초기화의 완료를 기다린다. 그래야 아직 실행 중인 초기화 코드가 있는데 그 코드를 담은 메모리를 먼저 해제하는 일이 없다. 이후 초기화용 메모리를 정리하고, 다음 장에서 다룰 init 선택과 exec로 진행한다.[1]
이전: 커널 초기화 · 다음: 루트 파일시스템과 init 선택
참고 문헌
- ↑ 1.0 1.1 1.2 1.3 1.4 1.5 v6.12 init/main.c, rest_init.
- ↑ 2.0 2.1 2.2 v6.12 kernel/fork.c, user_mode_thread, kernel_thread, kernel_clone.
- ↑ v6.12 fs/exec.c, kernel_execve.
- ↑ v6.12 init/initramfs.c, populate_rootfs 및 rootfs_initcall 등록.