운영체제 기본/x86-64 Linux의 BIOS 부팅/커널 초기화: 두 판 사이의 차이
출처·문체 정리: 커널 원문은 git.kernel.org, em dash는 콜론으로 변경 |
용어 통일: 시스템 콜, 컨텍스트 스위치와 첫 등장 영문 병기 |
||
| 13번째 줄: | 13번째 줄: | ||
또한 본체가 실제로 적재된 물리 위치가 빌드 시 예상한 위치와 다를 수 있다. <code>__startup_64()</code>는 그 차이를 반영해 초기 페이지 테이블을 보정한다. 어셈블리 코드는 CR3에 사용할 페이지 테이블 정보를 넣고 커널 가상 주소의 공통 시작 코드로 분기한다. '''파일을 올바르게 풀어 놓는 작업과 CPU가 올바른 주소 변환으로 그것을 실행하는 작업을 구별해야 한다.'''<ref name="head"/><ref name="headc"/> | 또한 본체가 실제로 적재된 물리 위치가 빌드 시 예상한 위치와 다를 수 있다. <code>__startup_64()</code>는 그 차이를 반영해 초기 페이지 테이블을 보정한다. 어셈블리 코드는 CR3에 사용할 페이지 테이블 정보를 넣고 커널 가상 주소의 공통 시작 코드로 분기한다. '''파일을 올바르게 풀어 놓는 작업과 CPU가 올바른 주소 변환으로 그것을 실행하는 작업을 구별해야 한다.'''<ref name="head"/><ref name="headc"/> | ||
여기서 새 사용자 프로세스가 만들어지는 것은 아니다. 여전히 부트 CPU에서 같은 초기 실행 흐름이 계속된다. 페이지 테이블이 바뀌었다는 사실만으로 사용자 모드 전환이나 | 여기서 새 사용자 프로세스가 만들어지는 것은 아니다. 여전히 부트 CPU에서 같은 초기 실행 흐름이 계속된다. 페이지 테이블이 바뀌었다는 사실만으로 사용자 모드 전환이나 컨텍스트 스위치(Context Switch)가 일어났다고 해석하지 않는다. | ||
== 2. x86 전용 준비를 마치고 공통 코드로 간다 == | == 2. x86 전용 준비를 마치고 공통 코드로 간다 == | ||
2026년 10월 31일 (토) 08:32 판
시작 상태: 압축 해제된 본체의 arch/x86/kernel/head_64.S:startup_64에 도착했다. CPU는 64비트 모드이며 초기 매핑과 부트 정보가 있다.
끝 상태: start_kernel()이 핵심 기반을 초기화하고, 별도 태스크를 만드는 rest_init()으로 넘어간다.
기준은 Linux v6.12의 x86-64 부트 CPU 경로다. “이미 커널 명령어를 실행한다”와 “프로세스를 스케줄링할 준비가 끝났다”는 서로 다른 상태다.
1. 본체가 도착했다고 실행 환경이 완성되지는 않는다
앞 장의 해제기는 본체를 배치해 주었다. 그러나 그 해제기의 스택과 임시 매핑을 그대로 영구적인 커널 환경으로 사용할 수는 없다. 본체는 자신의 초기 스택을 잡고 GDT/IDT를 준비하며 CPU 상태를 확인한다. GDT는 세그먼트 정보를, IDT는 예외·인터럽트 진입 정보를 제공한다. 이 시기의 준비는 이후 모든 기능이 갖춰진 상태와 같지 않다. 예외 처리 자체도 사용할 수 있는 초기화 수준에 맞아야 하기 때문이다.[1][2]
또한 본체가 실제로 적재된 물리 위치가 빌드 시 예상한 위치와 다를 수 있다. __startup_64()는 그 차이를 반영해 초기 페이지 테이블을 보정한다. 어셈블리 코드는 CR3에 사용할 페이지 테이블 정보를 넣고 커널 가상 주소의 공통 시작 코드로 분기한다. 파일을 올바르게 풀어 놓는 작업과 CPU가 올바른 주소 변환으로 그것을 실행하는 작업을 구별해야 한다.[1][2]
여기서 새 사용자 프로세스가 만들어지는 것은 아니다. 여전히 부트 CPU에서 같은 초기 실행 흐름이 계속된다. 페이지 테이블이 바뀌었다는 사실만으로 사용자 모드 전환이나 컨텍스트 스위치(Context Switch)가 일어났다고 해석하지 않는다.
2. x86 전용 준비를 마치고 공통 코드로 간다
어셈블리 공통 경로는 initial_code를 간접 호출한다. 부트 CPU에 대한 초기값은 x86_64_start_kernel이다. 다만 이것을 “최초의 C 함수”라고 부르면 앞에서 실행한 __startup_64() 같은 C 코드를 놓치게 된다. 이 함수의 의미는 C 언어의 시작점이라기보다 본체의 다음 초기화 단계로 넘어가는 경계에 가깝다.[1][2]
x86_64_start_kernel()은 초기 페이지 테이블을 정리하고 본체의 BSS를 비우며, 초기 예외 처리와 부트 정보 복사 등을 수행한다. BSS는 0으로 초기화되어야 하는 전역·정적 데이터 영역이다. 압축 해제기도 자신의 BSS를 비웠지만, 이제 다루는 것은 본체의 영역이다. 같은 이름의 준비가 반복된다고 해서 같은 메모리를 같은 이유로 두 번 처리하는 것은 아니다.[2]
이어 x86_64_start_reservations()가 플랫폼별 초기 처리를 거쳐 start_kernel()을 호출한다. 인자 이름 real_mode_data는 역사적 이름이지 현재 CPU의 실행 모드를 설명하는 문장이 아니다.[2]
중간 처리와 조건부 기능을 생략한 호출·분기 관계:
kernel/head_64.S:startup_64
→ __startup_64() 초기 페이지 테이블 보정
→ common_startup_64 커널 가상 주소에서 진행
→ initial_code가 가리키는 함수
x86_64_start_kernel()
→ x86_64_start_reservations()
→ start_kernel()
3. start_kernel은 무엇을 가능하게 만드는가
start_kernel()에는 아키텍처 준비, 메모리 관리, 스케줄러, 인터럽트·시간 관리, VFS 등 다양한 초기화가 모여 있다. 이름 목록보다 중요한 것은 다음 기능을 쓰기 전에 그 기능의 기반을 먼저 만든다는 의존 관계다.[3]
예를 들어 스케줄러는 타이머 인터럽트 같은 사건이 실행 흐름을 건드리기 전에 준비된다. 소스도 sched_init()을 인터럽트 시작보다 앞에 두는 이유를 명시한다. 초기 인터럽트 비활성 상태를 유지하며 필요한 준비를 진행한 뒤 local_irq_enable()에 도달한다. 이 순서는 단순한 함수 정렬이 아니라 아직 준비되지 않은 기능이 먼저 사용되는 일을 막는 순서다.[3]
그러나 start_kernel()이 끝났다고 모든 장치와 모든 CPU의 초기화가 끝나는 것은 아니다. 이 함수가 rest_init()으로 넘어가면 별도 태스크가 만들어지고, 이후 PID 1의 커널 실행 문맥에서 나머지 초기화가 이어진다. 따라서 부팅을 “전부 초기화한 다음 처음 프로세스를 만든다”로 요약하면 실제 경계를 놓친다.[3]
소스 탐색: __startup_64 · x86_64_start_kernel · start_kernel. 각주는 동일 태그의 커널 저장소 원문이다.
이전: 진입 규약과 압축 해제 · 다음: PID 1 생성
- ↑ 1.0 1.1 1.2 v6.12 arch/x86/kernel/head_64.S, startup_64, common_startup_64.
- ↑ 2.0 2.1 2.2 2.3 2.4 v6.12 arch/x86/kernel/head64.c, __startup_64, startup_64_setup_gdt_idt 및 초기 IDT에 관한 주석.
- ↑ 3.0 3.1 3.2 v6.12 init/main.c, start_kernel.