리눅스 부팅 과정: 전원 버튼에서 커널까지
(0xkato.xyz)- 컴퓨터의 전원 버튼을 누른 순간부터 리눅스 커널이 실행되기까지의 과정을 단계별로 설명한 기술 해설
- CPU가 리얼 모드(real mode) 에서 시작해 프로텍티드 모드(protected mode) 와 롱 모드(long mode) 로 진입하는 과정을 구체적으로 다룸
- BIOS/UEFI 펌웨어, 부트로더(GRUB) , 커널 압축 해제 및 주소 재배치 등 각 단계의 역할과 동작 원리를 상세히 기술
- 메모리 매핑, 인터럽트, 페이지 테이블, kASLR 등 커널 초기화에 필요한 핵심 개념을 간결한 예시와 함께 설명
- 리눅스 부팅의 내부 메커니즘을 이해함으로써 시스템 아키텍처, 보안, 성능 최적화에 대한 통찰을 제공
Part 1 — 전원 버튼에서 커널의 첫 실행까지
-
전원 버튼을 누르면 CPU가 리얼 모드(real mode) 로 리셋되어 초기 명령을 실행
- 리얼 모드는 8086 시절부터 존재한 단순한 주소 체계로, 세그먼트(segment) 와 오프셋(offset) 을 조합해 물리 주소를 계산
- 예:
physical_address = (segment << 4) + offset - CPU는 리셋 후
0xFFFFFFF0주소(리셋 벡터)로 점프해 펌웨어로 제어를 넘김
-
레지스터(register) 는 CPU 내부의 초고속 저장 슬롯로, CS(코드 세그먼트), IP(명령 포인터) 등이 있음
- CS는 현재 코드의 위치, IP는 다음 실행 명령을 가리킴
BIOS와 UEFI
-
BIOS 는 오래된 펌웨어로, POST(전원 자가 진단) 후 부팅 순서를 확인하고 부팅 가능한 디스크를 탐색
- 부팅 가능한 디스크는 첫 512바이트 섹터 끝이
0x55AA로 표시됨 - BIOS는 이 섹터를
0x7C00주소로 복사 후 점프해 실행
- 부팅 가능한 디스크는 첫 512바이트 섹터 끝이
-
UEFI 는 현대적 대체 기술로, 파일시스템을 직접 이해하고 더 큰 부트 프로그램을 로드 가능
- BIOS와 달리 “첫 섹터” 제약이 없으며, OS에 더 풍부한 시스템 정보를 전달
부트로더(bootloader)
-
부트로더 는 커널을 메모리에 적재하고 실행 준비를 하는 프로그램
- 대표적으로 GRUB 이 사용되며, 설정 파일을 읽고 커널과 초기 램디스크(initrd)를 메모리에 로드
- 커널 파일은 리얼 모드용 작은 설정 프로그램과 압축된 커널 본체로 구성
- GRUB은 커널 위치, 커맨드라인, initrd 위치 등의 정보를 setup header 구조체에 기록 후 커널 설정 코드로 점프
설정 프로그램(setup code)
- 커널 실행 전 예측 가능한 작업 공간을 만드는 역할 수행
- 세그먼트 레지스터(CS, DS, SS)를 정렬하고 direction flag 를 클리어해 메모리 복사가 일정하게 동작하도록 설정
- 스택(stack) 을 생성해 함수 호출 시 임시 데이터를 저장
- BSS 영역(초기값 0으로 시작해야 하는 전역 변수 공간)을 0으로 초기화
-
earlyprintk옵션이 있으면 시리얼 포트를 설정해 초기 디버그 메시지 출력 가능 - 펌웨어에 RAM 맵(e820) 을 요청해 사용 가능한 메모리 영역과 예약 영역을 파악
- 모든 준비가 끝나면 첫 C 함수인
main을 호출, 이후 모드 전환 단계로 진입
인터럽트(interrupt)
-
인터럽트 는 CPU가 현재 작업을 잠시 멈추고 긴급 처리를 수행하는 메커니즘
- 키 입력, 타이머 등 하드웨어 이벤트가 대표적
- 마스크 가능 인터럽트 는 일시 차단 가능, NMI(Non-Maskable Interrupt) 는 항상 처리됨
- 모드 전환 중에는 예기치 않은 인터럽트를 방지하기 위해 일시적으로 차단
Part 2 — 리얼 모드에서 32비트, 그리고 64비트로
- 현대 리눅스는 x86_64 아키텍처의 롱 모드(long mode) 에서 동작
- 리얼 모드 → 프로텍티드 모드 → 롱 모드 순으로 단계적 전환 필요
프로텍티드 모드(protected mode)
- 1980년대 한계를 넘기 위해 도입된 32비트 모드로, 두 가지 핵심 구조를 가짐
-
GDT(Global Descriptor Table) : 세그먼트의 시작 주소, 크기, 권한을 정의
- 리눅스는 플랫 모델(flat model) 을 사용해 전체 32비트 공간을 하나의 연속 영역으로 단순화
-
IDT(Interrupt Descriptor Table) : 인터럽트 발생 시 호출할 핸들러 주소를 저장
- 부팅 중에는 최소한의 IDT만 로드하고, 커널 초기화 후 완전한 IDT를 설치
-
GDT(Global Descriptor Table) : 세그먼트의 시작 주소, 크기, 권한을 정의
모드 전환 과정
- 설정 코드가 먼저 인터럽트 비활성화, PIC 칩 정지, A20 라인 활성화, 수학 보조 프로세서 초기화 수행
- A20 라인은 1MB 주소 래핑 문제를 해결하기 위한 역사적 장치
- 최소한의 GDT와 IDT를 로드한 뒤, CR0 레지스터의 PE 비트를 설정하고 far jump 수행
- 이로써 프로텍티드 모드 진입, 세그먼트 및 스택 포인터를 새 주소 체계에 맞게 재설정
제어 레지스터(control registers)
- CR0: 프로텍티드 모드 활성화
- CR3: 페이지 테이블의 최상위 주소 저장
- CR4: PAE 등 확장 기능 활성화
롱 모드(long mode) 진입 준비
- 64비트 모드로 전환하려면 두 가지 조건 필요
- 페이징(paging) 활성화: 가상 주소와 물리 주소 간 매핑 수행
- EFER 레지스터의 LME(Long Mode Enable) 비트 설정
- 페이지 테이블은 4KB 단위 페이지를 매핑하며, 초기 부팅 시에는 2MB 단위의 identity map 으로 단순 구성
페이징 활성화 절차
- PAE 기능을 CR4에서 켜고, 저주소 영역을 2MB 단위로 커버하는 최소 페이지 테이블 생성
- 최상위 테이블 주소를 CR3에 기록 후 페이징 활성화
- EFER의 LME 비트를 세팅하고 64비트 코드로 점프해 롱 모드 진입
- 주소와 레지스터가 64비트 폭으로 확장된 상태에서 커널 실행 준비 완료
Part 3 — 커널 압축 해제, 주소 수정, 그리고 자기 이동
- 이제 CPU는 64비트 모드이며, 메모리에는 압축된 커널 이미지 가 존재
- 작은 64비트 스텁(stub)이 커널을 해제하고 주소를 조정하는 역할 수행
초기 정리 및 안전 장치 설정
- 스텁은 자신의 실제 실행 위치를 계산하고, 커널이 겹칠 경우 자기 복사(self-relocation) 로 안전한 위치로 이동
- 자신의 BSS 영역 초기화, 간단한 IDT 로드 (페이지 폴트와 NMI 핸들러 포함)
- 페이지 폴트 발생 시 누락된 매핑을 즉시 추가해 복구
- 커널, 부트 파라미터, 커맨드라인 버퍼 등 필요한 영역에 대한 identity 매핑 생성
커널 압축 해제
-
extract_kernel함수가 실행되어 커널 압축을 해제- gzip, xz, zstd, lzo 등 다양한 압축 알고리듬 지원
- 해제 후 ELF 헤더 를 읽어 코드/데이터 섹션을 올바른 주소로 복사
- 커널이 빌드된 주소와 실제 로드 주소가 다를 경우 재배치(relocation) 수행
- 주소를 포함한 명령어나 포인터를 수정해 실제 메모리 위치에 맞춤
- 모든 준비가 끝나면
start_kernel함수 로 점프하며, 본격적인 커널 초기화 시작
커널의 자기 이동(kASLR)
-
kASLR (Kernel Address Space Layout Randomization) 은 커널의 물리·가상 주소를 무작위화해 공격 난이도 상승
- 부팅 시 두 개의 기준(base)을 무작위로 선택
- 물리적 베이스: 커널이 실제로 위치할 RAM 주소
- 가상 베이스: 커널이 사용할 가상 주소 시작점
- 부팅 시 두 개의 기준(base)을 무작위로 선택
- 선택 과정
- 부트로더, initrd, 커맨드라인 버퍼 등 보호해야 할 영역 목록 작성
- 펌웨어의 메모리 맵을 스캔해 충분히 큰 빈 영역 탐색
- 하드웨어 난수 명령 등에서 얻은 엔트로피 로 무작위 슬롯 선택
- 적절한 영역이 없으면 기본 주소로 복귀하며,
nokaslr옵션 시 무작위화 비활성화
용어 요약
-
Hexadecimal(16진수) :
0x접두어로 표시, 하드웨어 비트 구조와 정렬이 용이 - Register: CPU 내부의 임시 저장소 (CS, DS, SS, IP, SP 등)
-
Segment/Offset: 리얼 모드 주소 계산 방식
(segment * 16 + offset) - BIOS/UEFI: 시스템 초기화 및 부트 프로그램 로드 담당 펌웨어
- Bootloader(GRUB) : 커널 로드 및 시스템 정보 전달
- Stack/BSS: 함수 임시 저장소 및 0으로 초기화된 전역 변수 영역
- Interrupt/NMI: 하드웨어·소프트웨어 이벤트 처리 메커니즘
- GDT/IDT: 세그먼트 및 인터럽트 정의 테이블
- A20 Line: 1MB 주소 래핑 방지 스위치
- Protected Mode/Long Mode: 32비트 및 64비트 실행 모드
- Paging/Page Tables: 가상 주소와 물리 주소
Hacker News 의견
- 정말 좋은 글이었음. 몇 달 전 나도 Linux 부팅 과정에 대해 글을 썼는데, 디스크 I/O 측면(디스크에 무엇이 있고, 어떻게 로드되는지)에 좀 더 초점을 맞췄음
내 글은 Booting x86-64에서 볼 수 있음 - 페이지 소스에 이런 코드가 있었음
이게 뭐지 하는 느낌이었음<!-- Femboy Mode Button - Hidden on Mobile --> <button class="rave-button" id="raveButton" onclick="toggleRaveMode()" title="Femboy Mode" style="display: none;"> <span class="button-text">uwu</span> </button>- 아직 작업 중인 기능임
-
UEFI는 펌웨어가 구현한 인터페이스임. 즉, 펌웨어 자체가 아니라 펌웨어와 대화하기 위한 표준 인터페이스라는 뜻임
“UEFI가 머신을 시작한다”는 표현은 약간 잘못된 용어 사용임. 실제로는 펌웨어가 머신을 시작하고, 우리는 UEFI를 통해 펌웨어와 통신함
이 글은 현대 펌웨어의 흥미로운 부분들을 많이 생략하고 있음. 예를 들어ExitBootServices()를 호출할 때 이미 long mode에 진입해 있음. 굳이 real/protected 모드 전환 과정을 거칠 필요가 없음- 이런 내용을 더 읽을 수 있는 자료가 어디 있는지 궁금함
- 전원 버튼이 정말로 CPU를 직접 켜는지 궁금했음. 아마도 Intel Management Engine(또는 AMD의 유사 기능)이 항상 켜져 있다가 전원 버튼 신호를 받아 CPU를 시작시키는 구조일 수도 있음
- 그 역할은 Embedded Controller(EC) 가 담당한다고 생각함. 부트로더가 CPU를 올리기 전에 동작하는 부분임
Chromebook에서는 이 부분이 coreboot 부트로더와 함께 오픈소스로 공개되어 있음
관련 문서는 Chromium EC Zephyr README에서 볼 수 있음
- 그 역할은 Embedded Controller(EC) 가 담당한다고 생각함. 부트로더가 CPU를 올리기 전에 동작하는 부분임
- 이 글보다 좀 더 상세한 버전의 글을 찾고 있음. 마이크로프로세서 데이터시트를 통째로 읽고 싶진 않지만, UEFI/BIOS 이전 단계까지 좀 더 깊게 다루는 자료가 있으면 좋겠음
- 글이 흥미롭긴 한데, 한편으로는 왜 16진수(HEX) 설명까지 들어가야 하는지 의문이었음. 이런 수준의 글을 읽는 사람이 그걸 모를까 싶었음
또 한 가지 궁금한 점은, 물리적인 전원 버튼을 누르는 순간 어떻게 reset vector로 연결되는가 하는 부분임. 이건 하드웨어와 전자회로의 마법 같은 영역임- HN은 IT 전문가만 보는 곳이 아님. Linux 부팅 과정이 궁금하지만 16진수 개념을 잘 모르거나 잊은 사람도 있을 수 있음
- 주제는 흥미롭지만 너무 초보자 대상으로 느껴졌음
- “전원이 안정화되면 CPU는 real mode로 리셋된다” 같은 설명을 보면, 내 할머니가 이런 걸 이해할 정도로 능숙한 분인가 싶었음
- 대학에서 독자 분석(audience analysis) 을 배웠는데, 어떤 지식이 이미 있다고 가정할지, 어떤 용어나 약어를 풀어야 할지 판단하는 게 중요함. 기술 글쓰기에서 이건 일종의 예술임
- 모바일에서 읽기 힘들었음. 글자색이 너무 흐림
- 데스크톱 브라우저에서도 스타일링이 별로였음. Firefox나 Firefox Mobile의 리더 모드를 쓰면 훨씬 읽기 좋음
- 약간 자기비하식 디자인처럼 보였음
- GRUB이 언급되긴 했지만 자세히 다루지 않았음
관련해서는 Pixelbeat의 디스크 구조 문서가 도움이 됨 - 이 글을 보고 예전에 Facebook 기술 면접을 봤던 기억이 났음. 2010년쯤 Production Engineer 포지션 전화 인터뷰였는데, “Linux 서버의 부팅 과정을 설명하라”는 질문을 받았음
더 깊이 들어가서 설명하라는 힌트 외엔 아무 도움도 없었음. 결국 더블린으로 이사하지 않았고, 뭐 surveillance capitalism 어쩌고 하면서 포도는 아직 덜 익은 셈이었음