리눅스 부팅 과정: 전원 버튼에서 커널까지

6 days ago 7

  • 컴퓨터의 전원 버튼을 누른 순간부터 리눅스 커널이 실행되기까지의 과정을 단계별로 설명한 기술 해설
  • 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 주소로 복사 후 점프해 실행
  • 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를 설치

모드 전환 과정

  • 설정 코드가 먼저 인터럽트 비활성화, 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 주소
      • 가상 베이스: 커널이 사용할 가상 주소 시작점
  • 선택 과정
    • 부트로더, 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: 가상 주소와 물리 주소

Read Entire Article