- 커널 부팅의 시작과 제어권 이전
ARM 아키텍처 기반 시스템에서, U-Boot 등의 부트로더는 하드웨어 초기화(메모리, 클럭, 시리얼 포트 등)를 수행한 후, 커널 이미지(zImage 또는 Image)를 지정된 물리 주소(예: 0x40000000)로 로딩하고 해당 위치로 실행을 전달한다. 이 순간부터 제어권은 커널로 넘어가며, 부트로더는 'Starting kernel...' 메시지를 출력한 후 종료된다.
커널 이미지의 헤더에는 arch/arm/kernel/head.S에 정의된 어셈블리 코드가 포함되어 있으며, 이는 다음 작업을 수행한다:
- 초기 페이지 테이블 매핑(일치 매핑 및 커널 매핑 설정)
- CPU 모드 변경(SVC 모드로 전환), 중단 비활성화, MMU 비활성화
- 스택 포인터 초기화
- C 언어 진입점인
start_kernel()함수로 전환
이 함수는 init/main.c 파일에 정의되며, 커널의 첫 번째 C 코드 진입점이다. 함수 선언은 다음과 같다:
asmlinkage __visible void __init start_kernel(void)
asmlinkage는 파라미터 전달 방식을 스택 기반으로 보장하며, ARM AAPCS 규격에 맞춘다. __visible는 링커가 심볼을 인식할 수 있도록 하고, __init는 초기화 단계에서만 사용되며, 이후 free_initmem()에 의해 할당된 메모리(.init.text 섹션)가 해제됨을 의미한다. 커널의 생명주기는 start_kernel()의 첫 줄에서 시작하여, rest_init()가 생성한 idle 프로세스가 무한 루프에 들어가는 순간까지 이어진다. 이 시점에서 커널의 핵심 구조체는 완전히 초기화되었으며, 안정적인 운영 상태에 진입한다.
1.2 start_kernel(): 커널 초기화의 중심
start_kernel()는 커널 초기화의 총괄적 조율자 역할을 하며, 의존 관계를 고려해 순차적으로 실행된다. 우선 기본 환경을 구성하고, 핵심 서브시스템을 초기화한 후, 멀티스레드 프레임워크를 활성화한다. 아래는 실제 실행 순서에 따른 핵심 절차이며, 부팅 로그를 통해 검증 가능하다.
1.2.1 기초 환경 초기화
함수 시작 부분에서는 최소한의 실행 환경을 확보한다:
set_task_stack_end_magic(&init_task); // init_task 스택 끝부분 마법 값 설정 (스택 오버플로우 감지용)
smp_setup_processor_id(); // 현재 프로세서 ID 설정 (단일 코어 시스템에서는 0)
debug_objects_early_init(); // 디버깅 객체 초기화 (kmemleak 등에 사용)
local_irq_disable(); // 전역 중단 비활성화 (임계 영역 보호)
boot_cpu_init(); // 부트 프로세서 상태 표시
page_address_init(); // 고메모리 주소 매핑 초기화
pr_notice('%s', linux_banner); // 커널 버전 배너 출력 (로그 첫 줄)
여기서 pr_notice()가 작동하는 것은 커널이 이미 기본 출력 기능을 갖추고 있음을 의미한다. 그러나 console_init()가 아직 호출되지 않았기 때문에, 이 출력은 조기 printk 버퍼링 시스템(early_printk)의 결과물이다. head.S 내부에서 시리얼 포트 레지스터가 미리 설정되었으며, 간단한 printascii() 함수를 통해 직접 UART로 문자를 전송한다. console_init() 이전의 모든 printk() 호출은 이 시스템으로 리디렉션되며, 임시 저장 후 컨솔 시스템 준비 후 일괄 출력된다. 따라서 Booting Linux...와 같은 초기 로그 항목이 발생하는 원인을 설명한다.
1.2.2 아키텍처 및 커맨드 라인 처리
setup_arch(&command_line); // 아키텍처 특화 초기화 (ARM: DTB 해석, 메모리 구조 설정, 프로세서 특성 초기화)
add_latent_entropy(); // 시작 시 엔트로피 추가 (랜덤 숫자 생성기 초기값 제공)
add_device_randomness(command_line, strlen(command_line)); // 커맨드 라인을 엔트로피 소스로 활용
boot_init_stack_canary(); // 스택 캐나리 초기화 (스택 오버플로우 공격 방어)
setup_command_line(command_line); // 커맨드 라인 파싱 및 저장 (예: console=ttyS0,57600 root=/dev/mmcblk0p2)
setup_arch()는 아키텍처에 따라 결정되는 핵심 함수다. ARM 플랫폼에서는 장치 트리 블롭(DTB)을 해석하여 CPU 구성, 메모리 범위, 인터럽트 컨트롤러(GIC) 주소 등을 추출하고, 이를 바탕으로 memblock 메모리 할당 시스템을 구성한다. 커맨드 라인에서 받은 root=/dev/mmcblk0p2는 나중에 루트 파일시스템을 마운트하는 데 사용되는 중요한 정보로, 이후 초기 루트 파일시스템 탐색과 마운트 과정에 직접 영향을 준다.