임베디드 스터디 - 리눅스 부팅 과정
임베디드 스터디 - 리눅스 부팅 과정
리눅스 부팅 개요
- U-Boot가 커널 이미지를 메모리에 로드하고 진입점으로 점프한 이후부터, 첫 번째 사용자 프로세스(
/sbin/init)가 실행되기까지의 과정 - 크게 4단계로 구분됨
- zImage 자가 압축 해제
- 어셈블리 초기화
start_kernel()— 커널 서브시스템 초기화rest_init()— 첫 번째 프로세스 생성 및 사용자 공간 진입
1단계 — zImage 자가 압축 해제
- U-Boot가 zImage 진입점으로 점프하면 커널 본체 실행 전에 자가 압축 해제가 먼저 실행됨
- zImage는 gzip으로 압축된 커널 본체 + 자가 압축 해제 코드로 구성됨
- 압축 해제 코드가 메모리에 vmlinux(커널 본체)를 적재한 후 커널 진입점으로 점프
1
2
3
4
5
6
U-Boot → zImage 진입점으로 점프
↓
[자가 압축 해제 코드 실행]
gzip 압축 해제 → 메모리에 커널 본체 적재
↓
커널 본체 실행 시작
2단계 — 어셈블리 초기화
- C 코드(
start_kernel())로 넘어가기 전에 어셈블리로 CPU/메모리 기본 환경을 구성함- 이 시점엔 OS가 없으므로 모든 초기화를 직접 수행해야 함
1
2
3
4
5
6
7
8
[어셈블리 초기화 단계]
├── CPU 모드 설정 (Exception Level 등)
├── MMU·캐시 비활성화 ← 초기엔 꺼둬야 안전
├── 스택 포인터(SP) 설정 ← C 함수 호출을 위해 필수
├── DRAM 초기화
└── BSS 영역 초기화 ← 전역변수 0으로 클리어
↓
start_kernel() 호출
- 스택 포인터 설정이 가장 중요한 이유 : C 함수 호출 시 지역변수·리턴 주소·인자가 스택에 저장되므로, SP 없이는
start_kernel()호출 자체가 불가능
3단계 — start_kernel()
- 커널 핵심 서브시스템을 의존성 순서에 따라 초기화하는 C 함수
setup_arch()가 가장 먼저 실행되는 이유 : 메모리 맵을 알아야 이후mm_init()이 버디 시스템을 구축할 수 있음
1
2
3
4
5
6
7
start_kernel()
├── setup_arch() ← 아키텍처 초기화 (MMU 활성화, 메모리 맵, Device Tree 파싱)
├── mm_init() ← 메모리 관리자 초기화 (버디 시스템, 슬랩 할당자)
├── trap_init() ← 예외·인터럽트 벡터 설정
├── sched_init() ← 스케줄러 초기화
├── time_init() ← 타이머·클럭 초기화
└── rest_init() ← 첫 번째 프로세스 생성
mm_init()이 두 번째인 이유 : 스케줄러·인터럽트 초기화 과정에서kmalloc등 메모리 할당이 필요하므로, 메모리 관리자가 먼저 준비되어야 함
setup_arch() 세부 동작
arch/arm64/kernel/setup.c등 아키텍처별 코드를 호출- Device Tree(
.dtb) 파싱 → 보드 메모리 레이아웃, 주변장치 정보 파악 - MMU 활성화 → 가상 주소 체계 전환
4단계 — rest_init()
start_kernel()의 마지막 단계로, 커널 최초의 프로세스들을 생성함
1
2
3
4
5
rest_init()
├── kernel_thread(kernel_init) → PID 1 생성
├── kernel_thread(kthreadd) → PID 2 생성
└── 자신은 PID 0 (idle 프로세스) 로 남음
→ CPU가 할 일 없을 때 실행되는 무한루프
| PID | 이름 | 역할 |
|---|---|---|
| 0 | idle | rest_init() 자신. CPU 유휴 시 실행 |
| 1 | kernel_init | init 프로세스. 사용자 공간 진입점 |
| 2 | kthreadd | 커널 스레드 생성 데몬 |
kernel_init (PID 1) 동작
- 커널 공간에서 시작해 최종적으로 사용자 공간의
/sbin/init을 실행하는 것이 목표 /sbin/init은 파일이므로 파일시스템이 마운트되어 있어야 접근 가능- 이 시점엔 아직 아무 파일시스템도 마운트되지 않은 상태 → initramfs 활용
1
2
3
4
5
6
7
8
9
10
kernel_init (PID 1)
↓
initramfs를 임시 루트 파일시스템으로 마운트
↓
initramfs 안의 초기화 스크립트 실행
(모듈 로드, 실제 루트 파일시스템 마운트 준비)
↓
실제 루트 파일시스템(ext4 등)으로 전환 (pivot_root)
↓
execve("/sbin/init") → 커널 공간 → 사용자 공간 전환
- initramfs가 필요한 이유 : 스토리지 드라이버가
=m모듈인 경우, 파일시스템 마운트 전에 로드가 불가능한 닭-달걀 문제를 해결 (Day 51·59 참고)
전체 부팅 흐름 요약
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
U-Boot
↓
zImage 압축 해제 (자가 압축 해제 코드)
↓
어셈블리 초기화 (SP 설정, DRAM, BSS)
↓
start_kernel()
├── setup_arch() ← MMU, 메모리 맵, Device Tree
├── mm_init() ← 버디 시스템, 슬랩
├── trap_init() ← 인터럽트 벡터
├── sched_init() ← 스케줄러
└── time_init() ← 타이머
↓
rest_init()
├── PID 0 : idle 프로세스
├── PID 1 : kernel_init
└── PID 2 : kthreadd
↓
kernel_init (PID 1)
├── initramfs 마운트
├── 실제 루트 파일시스템으로 전환 (pivot_root)
└── execve("/sbin/init") → 사용자 공간 진입
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.