TL;DR
- 부팅은 계층적 부트스트래핑. 각 단계가 더 많은 기능을 활성화한 후 다음 단계로 제어를 넘긴다: Firmware (BIOS/UEFI) → Bootloader (GRUB) → Kernel → initramfs → systemd → Login.
- BIOS (1981): 16-bit real mode, 1 MB 주소 공간, MBR의 512바이트 첫 섹터. 현대엔 레거시.
- UEFI (2006+): 미니 운영체제. 64-bit, GPT 파티션, FAT32 파일시스템, Secure Boot, 자체 shell.
- GRUB (Grand Unified Bootloader): 멀티 OS 부팅, 커널 선택 메뉴. Stage 1 (MBR/EFI), Stage 1.5 (filesystem driver), Stage 2 (full UI).
- Kernel: 압축 해제, early init, 메모리/CPU 탐지, 드라이버 로드, init 프로세스 실행.
- initramfs: "이 커널이 실제 루트 파일시스템에 접근하려면 필요한 드라이버/스크립트". Chicken-and-egg 문제의 해결책.
- systemd (2010+): SysV init 대체. 병렬 서비스 시작, 의존성 DAG, socket activation, journal logging. 부팅 시간 수십 초 → 수 초.
- ARM / Raspberry Pi: BIOS 없음. 각 SoC가 자체 부팅 방식. Raspberry Pi는 GPU가 먼저 부팅하는 독특한 구조.
- Secure Boot / TPM / Measured Boot: 펌웨어 무결성 검증, 소프트웨어 체인 서명.
1. 부팅이란 무엇인가
1.1 "Bootstrapping"
"Boot"은 "bootstrap"(부트스트랩)의 축약. 원래 "자기 자신의 부트스트랩을 당겨 자기를 들어올린다"는 속담 — 불가능한 일을 비유.
컴퓨터 부팅은 실제로 그런 일을 한다:
- 전원 on → CPU에는 코드도 메모리도 없음.
- 아주 작은 프로그램이 실행되어 조금 더 큰 프로그램을 로드.
- 그 프로그램이 더 복잡한 시스템을 로드.
- 반복해서 최종적으로 운영체제가 실행.
각 단계가 다음 단계를 로드하는 계층적 부트스트래핑.
1.2 왜 이렇게 복잡한가
이상적으로는 "전원 on → OS 시작"이면 좋겠지만:
- 하드웨어가 미초기화: 메모리 컨트롤러, 디스크 컨트롤러, 네트워크 카드 모두 꺼진 상태.
- CPU의 시작 모드: x86은 16-bit real mode에서 시작 — 1981 IBM PC 호환성.
- 스토리지 다양성: 다양한 디스크 인터페이스 (SATA, NVMe, USB, network), 다양한 파일시스템 (ext4, xfs, btrfs, zfs).
- 드라이버 로드 필요: OS 자체가 드라이버를 가지고 있지만, 드라이버를 로드하려면 이미 파일시스템에 접근할 수 있어야 함 → chicken-and-egg.
각 단계가 이 문제들을 차례로 해결한다.
1.3 단계 요약
Power On
↓
Firmware (BIOS/UEFI)
- CPU 기본 초기화
- POST (Power-On Self-Test)
- 메모리/하드웨어 탐지
↓
Bootloader (GRUB)
- 커널과 initramfs 로드
- 부팅 옵션 제시
↓
Kernel
- 압축 해제
- 아키텍처 초기화
- 드라이버 로드
- init 실행
↓
initramfs
- 실제 루트 파일시스템 드라이버 로드
- 루트 파일시스템 마운트
- pivot_root
↓
systemd (PID 1)
- 의존성 있는 서비스 병렬 시작
- 사용자 공간 초기화
↓
Login / GUI
총 시간: 수 초 ~ 수십 초. 이 글은 각 단계를 자세히 본다.
2. x86 CPU 모드 전환
2.1 세 가지 모드
x86 CPU는 세 가지 실행 모드:
Real Mode (16-bit):
- 1981 IBM PC와 호환.
- 1 MB 주소 공간 (20-bit).
- 세그먼트 레지스터로 주소 계산.
- 메모리 보호 없음 — 아무 주소나 접근.
- CPU가 전원 on 시 이 모드에서 시작.
Protected Mode (32-bit):
- 80386 (1985)에서 도입.
- 4 GB 주소 공간.
- Segmentation + paging.
- Ring 0-3 특권 레벨.
- 메모리 보호, 가상 메모리.
Long Mode (64-bit):
- AMD64 (2003).
- 64-bit 주소 공간 (실제 48-bit = 256 TB).
- Protected mode의 확장.
- 대부분 세그먼트 폐기, paging만.
2.2 부팅 시 전환
전원 on:
- CPU가 real mode로 시작.
- 첫 명령:
CS:IP = F000:FFF0— BIOS ROM 매핑 위치. - BIOS (또는 UEFI)가 실행.
- 하드웨어 초기화.
- Bootloader에게 제어 이동 (real mode).
- Bootloader가 protected mode로 전환.
- 커널 로드.
- 커널이 long mode로 전환 (64-bit의 경우).
2.3 왜 이렇게 번거로운가
IBM PC 호환성 때문. 1981년 8086 → 80286 → 80386 → x86-64까지 40년의 누적.
각 세대가 이전 모드에서 시작해 전환. 레거시 support의 대가.
2023+ Intel이 x86S 제안 — 레거시 모드 제거, protected + long mode만. 하지만 아직 표준 아님.
3. 펌웨어 — BIOS와 UEFI
3.1 BIOS (Basic Input/Output System)
1981 IBM PC에서 시작. 펌웨어 ROM에 저장. 역할:
- POST (Power-On Self-Test): RAM, CPU, 주변기기 체크.
- 하드웨어 초기화: 메모리 컨트롤러, 주변기기.
- 부팅 디바이스 선택: 순서대로 디스크/USB/네트워크 시도.
- MBR 로드: 선택된 디바이스의 첫 섹터 (512 바이트)를
0x7C00에 로드, 실행.
단순하고 직접적. 40년 동안 동작.
3.2 BIOS의 한계
16-bit real mode 실행:
- 1 MB 주소 공간: 실제 RAM 많아도 BIOS는 1 MB만.
- 메모리 보호 없음: 어떤 프로그램도 BIOS를 덮어쓸 수 있음.
2 TB 디스크 한계:
- MBR은 32-bit LBA → 2^32 × 512 = 2 TB.
- 대용량 디스크에 부적합.
보안 없음:
- 임의 부팅 코드 허용.
- Bootkit 같은 멀웨어 취약.
확장성 제한:
- 정적 BIOS 설정.
- 드라이버 추가 어려움.
- 네트워크 부팅 제한적.
3.3 UEFI (Unified Extensible Firmware Interface)
Intel EFI (1998, Itanium용) → 2005년 표준화 → UEFI 브랜드 (2007).
본질: 미니 운영체제. 펌웨어가 훨씬 복잡.
특징:
- 32/64-bit: 현대 CPU 활용.
- FAT32 파일시스템 읽기 지원: 파일로 부팅 코드 저장.
- GPT 파티션: 2 TB 이상 디스크 지원.
- UEFI 드라이버: 펌웨어가 자체 드라이버 제공. 다른 OS도 활용 가능.
- EFI Shell: 커맨드 인터페이스, 디버깅.
- Secure Boot: 서명된 부팅 코드만 허용.
- Boot Manager: 여러 OS 엔트리 유지.
3.4 UEFI Boot Process
- SEC (Security): CPU reset vector. 핵심 초기화.
- PEI (Pre-EFI Initialization): 메모리 초기화, 기본 서비스.
- DXE (Driver Execution Environment): 드라이버 로드, 대부분 initialization.
- BDS (Boot Device Selection): 부트 매니저가 부팅할 것 선택.
- TSL (Transient System Load): OS 로더 실행.
- RT (Runtime): UEFI runtime services (OS가 활용 가능).
3.5 EFI System Partition (ESP)
UEFI 시스템에 필수인 파티션:
- FAT32 포맷.
- 보통
/boot/efi에 마운트. .efi파일들 저장: OS 로더, bootloader, 펌웨어 업데이트 등.- 크기: 100-500 MB.
예:
/boot/efi/
├── EFI/
│ ├── BOOT/
│ │ └── BOOTX64.EFI (기본 fallback)
│ ├── Microsoft/
│ │ └── Boot/
│ │ └── bootmgfw.efi (Windows Boot Manager)
│ ├── ubuntu/
│ │ └── grubx64.efi (GRUB for Ubuntu)
│ └── systemd/
│ └── systemd-bootx64.efi
UEFI가 NVRAM에 부팅 순서 저장. efibootmgr 명령으로 조회/수정.
3.6 Secure Boot
펌웨어가 부팅 코드의 서명을 검증. 신뢰할 수 있는 키로 서명된 것만 실행.
Firmware (trust anchor)
↓ verify signature
Bootloader (shim, GRUB)
↓ verify signature
Kernel + initramfs
각 단계가 다음을 검증 후 실행. 체인 중 하나라도 서명이 안 맞으면 중단.
Key types:
- PK (Platform Key): OEM이 제공.
- KEK (Key Exchange Key): OS 벤더 (Microsoft, Canonical).
- db: 허용 목록.
- dbx: 차단 목록 (revoked signatures).
Linux 배포판의 Secure Boot:
- Shim: Microsoft가 서명한 작은 loader. Shim이 distro-specific GRUB을 검증.
- 각 배포판이 자체 키 체인을 shim에 설정.
3.7 MBR vs GPT
MBR (Master Boot Record):
- 디스크 첫 섹터 (512 B).
- 446 B: boot code.
- 64 B: 4개 파티션 엔트리.
- 2 B: magic 0xAA55.
GPT (GUID Partition Table):
- GPT 헤더 + partition entries (일반적 128개).
- 각 partition에 GUID, 이름 (unicode).
- CRC32 보호, 백업 복사본.
- 2 TB 이상 지원.
- UEFI 필수.
현대 시스템은 모두 UEFI + GPT.
4. GRUB
4.1 개요
GRUB (GRand Unified Bootloader): Linux 진영의 표준 bootloader. 1995년 시작, GRUB 2가 현재 주류.
역할:
- 사용자에게 부팅 옵션 제시 (여러 OS, 여러 커널).
- 커널과 initramfs 로드.
- 커널 파라미터 전달.
- 커널에 제어 이동.
4.2 Legacy BIOS에서의 3단계
BIOS는 첫 섹터 (512 B)만 로드할 수 있다. GRUB은 이 제약을 극복하기 위해 여러 단계로 구성:
Stage 1: 512 B에 맞는 매우 작은 코드. Stage 1.5를 로드.
Stage 1.5: 파일시스템 드라이버 포함. ext4/xfs에서 Stage 2를 로드할 수 있음.
Stage 2: 완전한 GRUB. 메뉴, 쉘, 모듈 로드, 커널 부팅.
4.3 UEFI에서
UEFI는 FAT32 파일시스템을 읽을 수 있으므로 Stage 1/1.5 불필요.
grubx64.efi 파일 하나가 /boot/efi/EFI/<distro>/에 위치. 전체 GRUB 모듈이 포함.
UEFI가 직접 로드하고 실행.
4.4 grub.cfg
메인 설정 파일. /boot/grub/grub.cfg 또는 /boot/grub2/grub.cfg.
menuentry 'Ubuntu' {
load_video
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root abc123-...
linux /boot/vmlinuz-6.8.0-generic root=UUID=def456-... ro quiet splash
initrd /boot/initrd.img-6.8.0-generic
}
linux: 커널 파일.initrd: initramfs 이미지.- kernel parameters:
root=,ro,quiet, etc.
이 파일은 수동 편집 금지 — /etc/default/grub 편집 후 update-grub 실행 권장.
4.5 GRUB Shell
부팅 시 키 누르면 GRUB shell 진입:
grub> ls
grub> set root=(hd0,1)
grub> linux /vmlinuz root=/dev/sda1
grub> initrd /initrd.img
grub> boot
디버깅에 유용. 설정 깨졌을 때 수동 부팅.
4.6 Chainloading
GRUB이 다른 bootloader를 로드. Windows와 멀티 부팅 시:
menuentry 'Windows' {
set root='hd0,1'
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
Windows Boot Manager로 제어 이동.
4.7 대안
- systemd-boot (과거 gummiboot): 매우 단순, UEFI 전용.
systemd-boot의 대안으로 인기. - rEFInd: 그래픽 UI, 아이콘 표시.
- LILO: 레거시, deprecated.
- SYSLINUX / ISOLINUX: 부팅 가능 CD/USB.
5. 커널 로드와 초기화
5.1 Kernel Image
Linux 커널은 vmlinuz로 배포:
vm= virtual memory.linuz= Linux, z = compressed (gzip, bzip2, lzma, xz, zstd).
구조:
bzImage (부팅 가능 커널)
├── setup code (real mode, 16-bit)
├── protected mode kernel stub
└── compressed kernel
GRUB이 이 파일을 메모리에 로드하고 setup code로 점프.
5.2 커널 압축 해제
Setup code가:
- Protected mode로 전환.
- self-extracting: 압축된 커널을 메모리에 풀어놓음.
- Long mode로 전환 (64-bit).
- 풀린 커널로 점프.
압축된 커널은 3-5 MB. 압축 해제 후 수십 MB.
5.3 Early Init
커널의 start_kernel() 함수 (init/main.c):
asmlinkage __visible void __init start_kernel(void) {
// 1. Setup basic CPU state
set_task_stack_end_magic(&init_task);
smp_setup_processor_id();
// 2. Init page allocator
mm_init();
// 3. Init memory management
setup_arch(&command_line);
mm_init_owner();
setup_per_cpu_areas();
build_all_zonelists();
page_alloc_init_cpuhp();
// 4. Init scheduler
sched_init();
// 5. Init interrupts
init_IRQ();
init_timers();
hrtimers_init();
// 6. Enable interrupts
local_irq_enable();
// 7. Init console
console_init();
// 8. Load device drivers
driver_init();
// 9. Mount rootfs
vfs_caches_init();
mnt_init();
// 10. Start init process
rest_init(); // → kernel_thread(kernel_init, ...) → /sbin/init
}
각 단계가 커널의 한 서브시스템을 초기화. 순서가 중요 — 후속 단계가 이전 것에 의존.
5.4 printk와 dmesg
커널이 부팅 중 log를 출력:
[ 0.000000] Linux version 6.8.0 (build@host) ...
[ 0.000000] Command line: BOOT_IMAGE=/vmlinuz root=UUID=...
[ 0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 ...'
[ 0.012345] Initializing ACPI ...
[ 0.123456] Loaded X.509 cert 'Linux Kernel ...'
[ 0.234567] pci 0000:00:01.0: [vendor:device] ...
...
부팅 후 dmesg로 확인. 문제 디버깅의 1차 도구.
5.5 initcall 순서
커널의 드라이버와 서브시스템이 등록되는 순서:
early_initcall(...) // 0: 아주 일찍
pure_initcall(...) // 1
core_initcall(...) // 2
postcore_initcall(...) // 3
arch_initcall(...) // 4
subsys_initcall(...) // 5
fs_initcall(...) // 6
device_initcall(...) // 7: 대부분 드라이버
late_initcall(...) // 8: 가장 늦게
각 레벨이 순차 실행. 같은 레벨은 등록 순서대로.
5.6 PID 1 시작
모든 커널 초기화가 끝나면 user space로 제어 이동:
static void __init rest_init(void) {
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
// ...
}
static int __ref kernel_init(void *unused) {
// ...
// Try different init paths
if (execute_command) {
ret = run_init_process(execute_command);
}
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
}
/sbin/init이 PID 1 — 모든 user space 프로세스의 조상.
현대 시스템에서 /sbin/init은 보통 systemd의 심볼릭 링크.
6. initramfs — Chicken-and-Egg 해결
6.1 문제
커널이 루트 파일시스템을 마운트해야 /sbin/init을 실행할 수 있다. 하지만:
- 루트 파일시스템이
/dev/sda1(SATA 디스크)에 있다면 → SATA 드라이버 필요. - 드라이버는
/lib/modules/*/...파일에 있다 → 파일시스템에 접근 필요. - 파일시스템에 접근하려면 드라이버 필요 → 돌고 돈다.
이것이 chicken-and-egg.
6.2 해결: initramfs
initramfs: "초기 RAM 파일시스템". 커널이 부팅 시 메모리에 로드되는 미니 파일시스템.
- 작은 크기 (~30-100 MB).
- 필수 드라이버와 스크립트 포함.
- RAM에 압축 해제되어 초기 root filesystem으로 사용.
6.3 initrd vs initramfs
초기 Linux (2.6 이전): initrd (Initial RAM Disk).
- 블록 디바이스 에뮬레이션.
- 디스크 이미지 파일.
현대 Linux (2.6+): initramfs (Initial RAM File System).
- cpio 아카이브 (압축).
- 메모리에 직접 unpacking.
rootfs라는 특수 tmpfs가 초기 파일시스템.
initrd라는 용어는 여전히 사용하지만 실제론 initramfs.
6.4 Initramfs 내용
/
├── bin/
│ ├── busybox (미니 userland)
│ └── ...
├── sbin/
│ └── init (Busybox의 init 또는 스크립트)
├── lib/
│ └── modules/
│ └── 6.8.0/
│ └── ... (필수 커널 모듈: ahci, nvme, ext4, ...)
├── etc/
├── dev/
├── proc/
├── sys/
└── init (스크립트 또는 실행 파일)
주요 드라이버:
- Storage: ahci, nvme, virtio_blk, usb-storage.
- Filesystem: ext4, xfs, btrfs, zfs.
- Crypto: dm-crypt (LUKS 암호화 디스크).
- LVM: lvm 드라이버.
- RAID: mdadm.
6.5 Initramfs 스크립트
전형적인 init 스크립트:
#!/bin/sh
# 1. 가상 파일시스템 마운트
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
# 2. 커널 파라미터에서 root 찾기
for arg in $(cat /proc/cmdline); do
case $arg in
root=*)
ROOT="${arg#root=}"
;;
esac
done
# 3. 필요한 모듈 로드
modprobe ext4
modprobe nvme
modprobe dm-crypt # 암호화 사용 시
# 4. LUKS 해제 (암호화 시)
if [ -n "$CRYPTDEVICE" ]; then
cryptsetup luksOpen $CRYPTDEVICE root
fi
# 5. 실제 root 마운트
mount -o ro $ROOT /newroot
# 6. pivot_root: /newroot를 새 /로
exec switch_root /newroot /sbin/init
6.6 Pivot Root
switch_root (또는 pivot_root): 루트 파일시스템 변경.
- 현재 프로세스를
/newroot로 이동. - 이전 루트(initramfs) 언마운트 및 해제 — 메모리 회수.
/newroot의/sbin/init실행.
이제 실제 root filesystem 위에서 실행. Initramfs의 미션 완료.
6.7 생성
Debian/Ubuntu:
sudo update-initramfs -u
Fedora/RHEL:
sudo dracut --regenerate-all
커널 업데이트 시 자동으로 생성.
6.8 디버깅
부팅이 멈추면 커널 파라미터에 break=premount 또는 debug 추가 → initramfs 내부 shell.
(initramfs) mount
(initramfs) ls /dev/
(initramfs) cat /proc/cmdline
직접 문제 진단.
7. systemd — PID 1
7.1 역사
SysV init (1984): AT&T UNIX System V의 init. 수십 년 간 표준.
문제:
- 순차 시작: 모든 서비스가 한 줄로. 느림.
- 쉘 스크립트: 복잡, 버그 취약.
- Dependency: 수동 관리.
Upstart (Ubuntu, 2006): 이벤트 기반 init. 일부 개선.
systemd (2010): Lennart Poettering (Red Hat). 완전한 재설계. 논란이 있었지만 현재 사실상 표준.
7.2 주요 기능
1. 병렬 시작:
- 의존성 DAG 분석.
- 독립 서비스들을 동시 시작.
- 부팅 시간 극적 감소.
2. Socket Activation:
- 서비스가 listen하기 전에 systemd가 소켓 생성.
- 클라이언트가 연결하면 systemd가 버퍼링.
- 서비스 시작은 lazy — 첫 연결 시.
3. D-Bus 통합:
- 서비스 간 IPC.
- 단방향 systemd 제어.
4. Cgroups:
- 각 서비스를 cgroup으로 격리.
- 자원 제한, 통계.
5. Journald:
- 구조화된 binary 로그.
- 빠른 검색 (
journalctl).
6. Target 시스템:
- SysV의 "runlevel"을 대체.
multi-user.target,graphical.target, etc.
7.3 Unit Files
systemd의 설정 단위. .service, .socket, .mount, .timer 등.
예: /etc/systemd/system/myapp.service:
[Unit]
Description=My Application
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=myapp
ExecStart=/usr/bin/myapp
Restart=on-failure
Environment="NODE_ENV=production"
WorkingDirectory=/var/lib/myapp
[Install]
WantedBy=multi-user.target
섹션:
[Unit]: 메타데이터, 의존성.[Service]: 실행 방법.[Install]: 어떤 target에 속하는지.
systemctl enable myapp → multi-user.target.wants/에 심볼릭 링크 생성 → 부팅 시 자동 시작.
7.4 Target Unit
여러 service를 묶는 "부팅 모드":
emergency.target: 최소 응급 모드 (초기 실패 시).rescue.target: 단일 사용자 모드.multi-user.target: 여러 사용자, 네트워크, 서비스 (non-GUI).graphical.target: GUI 포함.reboot.target: 재부팅.
각 target이 Wants= 또는 Requires=로 의존성.
7.5 부팅 순서
systemd PID 1 시작
↓
sysinit.target (기본 filesystem 마운트, udev, 등)
↓
basic.target (사용자 공간 기본 준비)
↓
multi-user.target (서비스 시작)
↓
graphical.target (디스플레이 매니저, optional)
7.6 Parallel Startup
전통 SysV:
systemd: nginx → 1s
systemd: postgres → 3s
systemd: redis → 2s
systemd: myapp → 2s (postgres 기다림)
총: 8s
systemd:
병렬 시작:
nginx |----| 1s
postgres |------| 3s
redis |--| 2s
myapp |--| 2s (postgres 후)
총: ~5s
DAG 기반 스케줄링. 최대 병렬도.
7.7 Socket Activation
더 공격적: 서비스가 시작되기 전에 소켓 생성.
# myapp.socket
[Unit]
Description=MyApp Socket
[Socket]
ListenStream=8080
[Install]
WantedBy=sockets.target
# myapp.service
[Unit]
Description=MyApp
Requires=myapp.socket
[Service]
ExecStart=/usr/bin/myapp
systemd가:
- 부팅 즉시
myapp.socket의 8080 포트 listen 시작. - 클라이언트가 연결하면 service 시작 트리거.
- 소켓을 service에 전달 (파일 디스크립터 상속).
장점:
- Lazy 시작: 실제 사용될 때만 서비스 시작.
- Graceful restart: 서비스 재시작 중 새 연결이 큐에 대기.
- 더 빠른 부팅: 모든 서비스를 완전히 시작 안 해도 네트워크 준비 완료.
inetd의 현대적 부활.
7.8 journalctl
journalctl # 모든 로그
journalctl -u nginx # nginx 서비스만
journalctl -b # 이번 부팅만
journalctl -f # follow (tail -f)
journalctl --since "1 hour ago"
journalctl -p err # 에러만
journalctl -k # 커널 로그 (dmesg 유사)
Binary format — 빠른 검색, 구조화된 필드.
7.9 시각화
systemd-analyze # 총 부팅 시간
systemd-analyze blame # 각 서비스가 걸린 시간
systemd-analyze critical-chain # 병목 분석
systemd-analyze plot > boot.svg # SVG chart
부팅 프로파일링 도구 내장.
7.10 논란
systemd는 많은 비판을 받았다:
- "Unix 철학 위반" (작은 도구들 조합 vs 거대한 시스템).
- "기능 폭증": systemd가 점점 많은 일을 함 (network, DNS, login 등).
- "복잡도": 이해 어려움.
대안:
- OpenRC (Alpine, Gentoo): 경량 shell 기반.
- runit: 매우 단순, Void Linux.
- s6: 더 모듈러.
하지만 사실상 표준: Debian, Ubuntu, Fedora, Arch, SUSE 등 주요 배포판이 systemd.
8. ARM / Raspberry Pi 부팅
8.1 ARM의 차이
ARM SoC는 BIOS/UEFI가 없다 (일부 제외). 각 칩이 자체 boot ROM을 가짐.
전형적 단계:
- Boot ROM (SoC 내부): 하드코딩된 가장 빠른 코드.
- Secondary Program Loader (SPL): 메모리 초기화, 드라이버 기본.
- U-Boot: 범용 embedded bootloader.
- Linux kernel + device tree.
- initramfs → rootfs.
8.2 Device Tree
ARM에는 BIOS가 하드웨어 정보를 제공하지 않는다. 대신 Device Tree Blob (DTB) 파일.
DTB: "이 시스템에 있는 하드웨어 목록"을 tree 구조로.
soc {
uart0: serial@10010000 {
compatible = "ns16550a";
reg = <0x10010000 0x100>;
clock-frequency = <50000000>;
};
ethernet@10040000 {
compatible = "cadence,gem";
reg = <0x10040000 0x2000>;
};
...
};
커널이 DTB를 읽고 어떤 드라이버를 로드할지 결정.
U-Boot이 커널과 함께 DTB 전달.
8.3 Raspberry Pi — 독특한 부팅
Raspberry Pi는 GPU가 CPU보다 먼저 부팅:
- GPU가 내부 ROM 실행.
- GPU가 SD 카드의
bootcode.bin읽기. - GPU가
start.elf(GPU firmware) 로드. - GPU가 CPU를 초기화하고 kernel 로드.
- CPU가 Linux 실행.
이상해 보이지만 Broadcom SoC의 특성.
SD 카드 파티션:
- FAT32 boot partition:
bootcode.bin,start.elf,config.txt, kernel, DTB. - ext4 root partition.
8.4 U-Boot
Universal Boot Loader. ARM, MIPS, RISC-V 등 embedded에 표준. 기능:
- 다양한 bootable 포맷 지원 (kernel, tftpboot, etc.).
- 네트워크 부팅 (TFTP, NFS).
- 환경 변수.
- 쉘 인터페이스.
=> printenv
=> setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2"
=> fatload mmc 0:1 0x80000 zImage
=> fatload mmc 0:1 0x8000000 dtb
=> bootz 0x80000 - 0x8000000
8.5 UEFI on ARM?
일부 ARM 서버(ARM ServerReady)는 UEFI 지원. Red Hat, Ubuntu가 ARM UEFI 부팅 가능.
Cavium/Marvell ThunderX, Ampere Altra, AWS Graviton (내부적으로 UEFI).
Embedded (Raspberry Pi, Rockchip 등)는 대부분 U-Boot.
9. Secure Boot와 Measured Boot
9.1 Secure Boot
위에서 설명. 서명 체인으로 부팅 코드 검증.
Chain of Trust:
UEFI firmware (OEM 신뢰)
↓ (verify signature)
Bootloader (shim, GRUB)
↓ (verify signature)
Kernel
↓ (verify signature) — kernel lockdown mode
Kernel modules
각 단계가 다음을 검증 없이 실행 안 함.
9.2 Kernel Lockdown
Linux 5.4+. Secure Boot 활성화 시 자동:
- 커널 메모리 read/write 금지.
/dev/mem,/dev/kmem접근 제한.- 서명 없는 커널 모듈 거부.
- Kexec 제한.
공격자가 root여도 커널 무결성 유지.
9.3 TPM — Trusted Platform Module
하드웨어 보안 칩:
- PCRs (Platform Configuration Registers): 측정값 저장.
- Endorsement Key: 고유 식별자.
- Sealed storage: 특정 PCR 값일 때만 해독 가능.
UEFI + TPM으로 Measured Boot 구현.
9.4 Measured Boot
Secure Boot과 다른 접근:
Secure Boot: "나쁜 코드를 막는다" — 서명 없으면 거부. Measured Boot: "좋은 코드를 증명한다" — 각 단계의 해시를 TPM에 기록. 나중에 검증.
Firmware → [hash → PCR 0]
Bootloader → [hash → PCR 2]
Kernel → [hash → PCR 4]
initramfs → [hash → PCR 7]
부팅 후:
- PCR 값을 remote attestation으로 증명.
- "이 기계는 지정된 정확한 코드로 부팅했다"를 확인.
9.5 Full Disk Encryption + TPM
사용자 로그인 없이:
TPM이 PCR 값 확인 → 정확하면 decryption key 해제 → 디스크 마운트
Measured boot + TPM-sealed keys로 사용자 개입 없는 자동 해독 + 무결성 보장.
Windows BitLocker, Linux systemd-cryptenroll이 이 패턴.
9.6 공격 시나리오
공격자가 physical access로:
- 부팅 미디어 교체 → Secure Boot이 서명 없다고 거부.
- 메모리 cold boot 공격 → 암호화 키 추출 시도. TPM으로 완화.
- Bootloader 교체 → 해시가 달라짐 → Measured Boot에서 detect.
완벽하진 않지만 많은 공격을 차단.
10. 부팅 시간 최적화
10.1 측정
systemd-analyze
# Startup finished in 1.2s (firmware) + 2.5s (loader) + 4.1s (kernel) + 6.3s (userspace) = 14.1s
systemd-analyze blame
# 2.145s NetworkManager.service
# 1.832s docker.service
# ...
systemd-analyze critical-chain
# (서비스의 의존성 체인으로 병목)
10.2 일반 기법
1. 불필요한 서비스 비활성화:
systemctl disable bluetooth.service
systemctl mask avahi-daemon.service
2. 네트워크 기다리지 않기:
[Unit]
After=network-online.target
Wants=network-online.target
만약 네트워크가 필요 없다면 network.target만.
3. 병렬도 확인: 의존성이 과도하면 병렬성 감소.
4. lazy loading: Socket activation 활용.
5. initramfs 최적화:
- 불필요한 드라이버 제외.
- 압축 알고리즘 선택 (zstd가 가장 좋음).
10.3 커널 최적화
- 불필요한 드라이버 제거: 맞춤 커널 빌드.
noatime: 파일 접근 시간 업데이트 안 함.fastboot커널 파라미터.
10.4 firmware 최적화
- Fast Boot (UEFI 설정): POST 간소화.
- 초기화된 하드웨어 재탐지 skip.
10초 → 2-3초까지 줄이는 경우 있음.
10.5 사례
Google Chromebook: 전원 on → 로그인 화면 8초. 아래 최적화:
- 자체 coreboot 펌웨어 (UEFI 대신).
- 최소 부팅 의존성.
- 병렬 서비스 시작 극대화.
- SSD + 빠른 firmware.
서버 환경: 20-30초. 데스크톱: 10-15초. 수 초 수준은 매우 잘 튜닝한 것.
11. 디버깅
11.1 커널이 안 뜰 때
GRUB에서 멈춤:
- GRUB 설정 오류.
- 커널 파일 없음.
- 디스크 UUID 변경.
해결: GRUB shell에서 수동 부팅, /etc/default/grub 확인.
Kernel panic:
- 루트 파일시스템 못 찾음.
- 필수 드라이버 missing (initramfs 불완전).
- 커널 메모리 문제.
해결: dmesg 확인 (이전 부팅), rescue 모드.
Initramfs shell ((initramfs)):
(initramfs) cat /proc/cmdline
(initramfs) ls /dev/
(initramfs) modprobe <missing-module>
(initramfs) exit
11.2 systemd가 시작 안 할 때
Emergency shell:
systemd.unit=emergency.target커널 파라미터.- 또는 GRUB에서
e눌러 편집.
Single user mode:
systemd.unit=rescue.target.- Root password 요구.
Boot analysis:
journalctl -b -p err # 에러만 이번 부팅
journalctl -xe # 최근 로그 상세
systemctl --failed # 실패한 서비스
11.3 커널 파라미터
부팅 중 GRUB에서 커널 라인 수정 (e 키):
init=/bin/bash: systemd 우회, bash 직접 실행.systemd.debug-shell: tty9에 debug shell.debug: 상세 부팅 로그.single: single user 모드.rescue: rescue target.nomodeset: KMS 비활성 (그래픽 문제).noapic,nolapic: APIC 비활성 (interrupt 문제).
11.4 로그 위치
/var/log/dmesg - 커널 buffer
/var/log/boot.log - 부팅 로그
/var/log/syslog - 시스템 로그
/var/log/journal/ - systemd journal
journalctl이 이들 대부분을 통합.
11.5 Live USB 복구
부팅 완전 실패 시:
- Live USB 부팅.
- 손상된 디스크 mount.
- chroot 진입.
- 수리 작업 (GRUB 재설치, 패키지 수정 등).
sudo mount /dev/sda2 /mnt
sudo mount /dev/sda1 /mnt/boot/efi
for i in /dev /proc /sys /run; do
sudo mount --bind $i /mnt$i
done
sudo chroot /mnt
# 여기서 수리
12. 현대 트렌드
12.1 Immutable / Atomic Systems
Fedora Silverblue, OpenSUSE MicroOS, CoreOS:
- 루트 파일시스템이 read-only.
- 업데이트는 atomic: 새 이미지 교체, 재부팅.
- 변경된 파일은 overlay로.
부팅 과정은 대체로 같지만 rootfs 관리가 다름.
12.2 systemd-boot 성장
GRUB 대안으로 systemd-boot (이전 gummiboot) 인기:
- 단순 — UEFI 전용, 몇 백 줄 코드.
- 빠름.
- 설정 쉬움 (
loader.conf).
Arch Linux, Fedora의 일부 워크스테이션이 기본.
12.3 Unified Kernel Image (UKI)
Kernel + initramfs + cmdline을 단일 EFI 바이너리로.
myapp.efi (EFI application)
├── Linux kernel
├── initramfs
└── default command line
장점:
- 단일 파일 관리.
- Secure Boot: 하나만 서명.
- Measured Boot: 단일 측정.
systemd-stub으로 생성.
12.4 TEE (Trusted Execution Environment)
Intel SGX, ARM TrustZone. 부팅 초기부터 격리된 실행 환경.
Confidential Computing: 클라우드에서 guest VM도 서비스 제공자로부터 격리. Azure CC, GCP Confidential VMs.
12.5 Firecracker / MicroVMs
위 hypervisor 포스트에서 본 Firecracker. 125 ms boot. Minimal kernel + minimal init.
전통 부팅 개념의 축소판 — 필요 없는 것 전부 제거.
13. 학습 리소스
책:
- "Linux Bootcamp: A Survival Guide" — Linux 부팅의 실전 가이드.
- "Understanding the Linux Kernel" — Daniel Bovet, Marco Cesati.
공식 문서:
- Linux kernel
Documentation/admin-guide/. - systemd 공식 문서 (
man systemd). - GRUB manual.
영상:
- Greg Kroah-Hartman의 Linux kernel 발표들.
- Lennart Poettering의 systemd 발표.
실습:
- QEMU로 custom kernel 빌드 후 부팅.
- 자체 initramfs 작성.
- systemd unit 작성 및 디버깅.
14. 요약 — 한 장 정리
┌─────────────────────────────────────────────────────┐
│ Linux Boot Cheat Sheet │
├─────────────────────────────────────────────────────┤
│ 순서: │
│ Power On │
│ → Firmware (BIOS or UEFI) │
│ → Bootloader (GRUB) │
│ → Kernel │
│ → initramfs │
│ → systemd (PID 1) │
│ → Login │
│ │
│ CPU 모드 (x86): │
│ Real Mode (16-bit) — 시작 │
│ Protected Mode (32-bit) │
│ Long Mode (64-bit) │
│ │
│ BIOS: │
│ 16-bit, 1 MB │
│ MBR (512 B first sector) │
│ 2 TB disk 한계 │
│ 보안 없음 │
│ │
│ UEFI: │
│ 미니 OS │
│ FAT32 filesystem │
│ GPT partition (2TB+) │
│ Secure Boot │
│ EFI System Partition (ESP) │
│ efibootmgr로 관리 │
│ │
│ GRUB: │
│ Stage 1, 1.5, 2 (legacy) │
│ grubx64.efi (UEFI) │
│ /boot/grub/grub.cfg │
│ Chainloading (Windows 등) │
│ │
│ Kernel: │
│ vmlinuz (compressed) │
│ start_kernel() │
│ initcall 레벨들 │
│ /sbin/init 실행 │
│ │
│ initramfs: │
│ Chicken-and-egg 해결 │
│ CPIO 압축 아카이브 │
│ 필수 드라이버 + scripts │
│ pivot_root / switch_root │
│ update-initramfs (Debian) │
│ dracut (Fedora) │
│ │
│ systemd: │
│ Lennart Poettering, 2010 │
│ Parallel service startup │
│ Dependency DAG │
│ Socket activation │
│ Cgroups 격리 │
│ Journald (binary logs) │
│ Unit files (.service, .socket, .timer) │
│ Targets (multi-user, graphical) │
│ systemctl, journalctl │
│ │
│ Secure Boot: │
│ UEFI 펌웨어가 서명 검증 │
│ Shim → GRUB → kernel → 모듈 │
│ PK, KEK, db, dbx │
│ │
│ Measured Boot: │
│ TPM PCR에 해시 기록 │
│ Remote attestation │
│ Sealed storage │
│ │
│ ARM / Raspberry Pi: │
│ BIOS 없음 │
│ U-Boot + Device Tree │
│ RPi: GPU가 먼저 부팅 │
│ │
│ 최적화: │
│ systemd-analyze blame │
│ 불필요 서비스 비활성 │
│ Socket activation │
│ Initramfs 압축 (zstd) │
└─────────────────────────────────────────────────────┘
15. 퀴즈
Q1. 커널이 initramfs를 필요로 하는 근본적 이유는?
A. Chicken-and-egg 문제: 루트 파일시스템에 접근하려면 드라이버가 필요하고, 드라이버는 파일시스템에 있다. 부팅 시나리오: 루트 파일시스템이 /dev/nvme0n1p2 (NVMe SSD)에 ext4로 있음. 커널은 /sbin/init을 실행해야 하는데, 이 파일에 접근하려면 (1) NVMe 드라이버가 로드되어야 하고, (2) ext4 드라이버가 로드되어야 한다. 그런데 이 드라이버들 자체가 /lib/modules/*/kernel/drivers/...에 있다 → 루트 파일시스템에 접근해야 드라이버를 얻을 수 있고, 드라이버가 있어야 루트 파일시스템에 접근할 수 있다. 해결: initramfs는 "커널이 부팅 시 함께 로드하는 미니 파일시스템". 메모리에 cpio 압축 해제되어 rootfs라는 임시 파일시스템이 된다. 필요한 드라이버들이 미리 포함되어 있어, 커널이 실제 루트 파일시스템에 접근할 수 있게 된다. 그 후 pivot_root/switch_root로 실제 루트로 전환하고 initramfs는 해제. 이것이 있어서 하나의 커널 이미지가 다양한 하드웨어 구성에서 부팅 가능하다 — 빌드 시점에 드라이버를 결정할 필요 없음.
Q2. UEFI가 BIOS를 대체한 근본적 이유는?
A. 1981년 BIOS 설계의 한계가 현대 컴퓨터에 맞지 않음. BIOS 한계: (1) 16-bit real mode 실행 → 1 MB 주소 공간만 사용 가능. 수십 GB RAM이 있어도 부팅 시 1 MB만. (2) MBR의 32-bit LBA → 디스크 크기 2 TB 한계. 현대 8 TB 디스크에 부적합. (3) 보안 없음 → MBR의 임의 코드를 신뢰 없이 실행. Bootkit 멀웨어에 취약. (4) 단순한 인터페이스 → 드라이버 확장 어려움, 현대 하드웨어 지원 제한. (5) Legacy 16-bit 코드 → 현대 컴파일러와 호환성. UEFI (2007+): "미니 운영체제"로 재설계. (1) 32/64-bit, 전체 메모리, (2) GPT로 무제한 디스크 크기, (3) Secure Boot로 서명 검증, (4) FAT32 파일시스템 지원 (바이너리를 파일로 저장, MBR 첫 섹터에 의존 안 함), (5) 확장 가능한 드라이버, 자체 shell, 네트워크 부팅 등. 대가: 복잡도 증가 — BIOS가 수 KB 코드였다면 UEFI는 MB급 코드베이스. "펌웨어 버그"라는 새 범주 생성. 하지만 현대 요구를 만족하려면 불가피. 2012년 이후 새 PC는 거의 모두 UEFI.
Q3. systemd의 "parallel service startup"이 어떻게 작동하는가?
A. 의존성 DAG 분석 + 독립 서비스 동시 시작. 전통 SysV init: 서비스들을 순차적으로 시작 — A → B → C → D. 한 서비스가 느리면 전체가 지연. 총 시간 = 각 서비스 시간의 합. systemd: 각 unit 파일에 After=, Requires=, Wants=로 의존성 명시. systemd가 부팅 시 전체 의존성 DAG를 빌드하고 topological sort로 시작 순서 결정. 의존성 없는 서비스들은 동시 시작. 예: nginx는 아무 것도 의존 안 함, postgres는 아무것도 의존 안 함 → 둘 다 즉시 시작. myapp은 After=postgres → postgres 준비 후 시작. 결과: 총 시간 = 의존성 chain의 최대 길이. 8초가 5초로 단축. 추가로 socket activation: systemd가 서비스를 실제 시작하기 전에 그 서비스의 listening 소켓을 먼저 만듦. 다른 서비스가 연결을 시도하면 소켓에 버퍼링. 서비스는 첫 연결 시에만 lazy 시작 → 전체 시스템 준비 전에 "네트워크 준비"가 가능. 부팅 시간 30초 → 5초 이하는 이 두 기법 덕분. Lennart Poettering의 핵심 공학적 기여 — 논란이 있지만 이 성능 향상만큼은 반박 불가.
Q4. Raspberry Pi가 "GPU가 먼저 부팅"하는 이유는?
A. Broadcom SoC의 독특한 설계. 일반 x86 부팅: CPU가 firmware ROM → bootloader → kernel 순서. Raspberry Pi의 BCM2835 (및 후속 칩): VideoCore GPU가 ARM CPU보다 먼저 부팅. 이유: (1) Broadcom은 원래 셋톱박스용 칩 만들었는데 그 설계에서 GPU가 주도권. VideoCore가 메인 control processor 역할. (2) ARM core가 초기 상태에서 SD 카드나 메모리 컨트롤러를 사용할 수 없음 — GPU가 이미 이 능력을 가지고 있음. (3) 비용 절감 — ARM용 별도 boot ROM 불필요. 부팅 순서: (1) GPU의 내부 boot ROM 실행, (2) GPU가 SD 카드 FAT 파티션에서 bootcode.bin 로드, (3) GPU가 start.elf (GPU firmware + 일부 driver) 로드, (4) GPU가 config.txt 읽고 ARM CPU에 메모리 할당, (5) GPU가 Linux kernel을 ARM 메모리에 복사, (6) GPU가 ARM CPU를 reset하고 kernel 시작. ARM 관점: "깨어나 보니 메모리에 커널이 이미 있다". SD 카드 boot partition 내용: bootcode.bin, start.elf, config.txt, kernel7.img, DTB 파일들. 이상해 보이지만 효과적 — RPi가 저렴한 이유 중 하나. RPi 4+는 이 구조가 약간 바뀌어 EEPROM에 boot loader.
Q5. Secure Boot과 Measured Boot의 차이는?
A. "막기"와 "증명하기"의 철학 차이. Secure Boot: UEFI 펌웨어가 각 부팅 단계(bootloader, kernel, driver)의 디지털 서명을 검증. 서명이 유효하지 않으면 실행 거부. 장점: 알려진 "나쁜 코드" 차단. 단점: (1) 허용 목록에 없는 합법 OS도 차단(리눅스 커뮤니티가 shim으로 우회), (2) "서명만 맞으면 OK" — 서명된 코드가 나중에 손상되어도 감지 안 됨, (3) 런타임 공격 감지 불가. Measured Boot: 각 부팅 단계의 해시를 계산해 TPM의 PCR(Platform Configuration Register)에 누적 기록. 실행은 막지 않음 — 실행 후 어느 시점에든 "이 기계가 정확히 어떤 코드로 부팅했는가"를 PCR 값으로 증명 가능. Secure Boot는 "방어" (block bad code), Measured Boot는 "감사" (prove what ran). 두 기술은 보완적: Secure Boot로 알려진 위협 차단 + Measured Boot로 실제 부팅 상태 증명. 실무 응용: (1) BitLocker (Windows)가 TPM sealed key 사용 — PCR 값이 일치할 때만 디스크 decryption key 해제. 공격자가 부팅 경로를 바꾸면 decrypt 실패. (2) Remote attestation: 클라우드 서버가 고객에게 "이 서버는 서명된 코드로만 부팅됐다"를 증명. 데이터센터 보안의 기반. 둘 다 CPU + TPM 2.0 필수 — Windows 11의 요구사항이 이 때문.
Q6. GRUB의 "Stage 1/1.5/2" 구조가 왜 필요했는가?
A. BIOS의 512-byte 제약과 파일시스템 접근의 모순 해결. Legacy BIOS는 MBR의 첫 512 바이트만 로드한다 (1981 설계 제약). 이 512 바이트 안에 "파일시스템에서 커널을 찾아 로드하는 코드"를 넣을 수 없다 — ext4 드라이버만 해도 수십 KB. GRUB의 해결책: 3단계 부트스트랩. (1) Stage 1: 정확히 512 바이트. MBR에 위치. 매우 단순 — "다음 몇 섹터를 로드해라"만. 파일시스템을 모름. (2) Stage 1.5: 디스크의 "BIOS boot partition" 또는 MBR 직후 빈 공간에 저장. 수십 KB 크기. 파일시스템 드라이버 포함 (ext4, xfs, btrfs 등). Stage 2를 파일시스템에서 읽을 수 있음. (3) Stage 2: /boot/grub/에 파일로 저장. 완전한 GRUB — 메뉴, 쉘, 모듈 로드, 커널 부팅. UEFI는 이 구조 불필요 — UEFI 자체가 FAT32를 읽을 수 있고 512-byte 제약도 없다. /boot/efi/EFI/<distro>/grubx64.efi 하나의 파일이 전체 GRUB이다. 그래서 UEFI 시스템에서는 "GRUB이 간단하다"고 느껴진다. Stage 1/1.5는 BIOS의 유산 — 2025년에도 legacy 시스템에서 볼 수 있지만 점차 사라짐. Debian 같은 배포판이 여전히 BIOS + GPT + Stage 1.5 지원을 유지하는 것은 오래된 하드웨어 호환성 때문.
Q7. systemd의 "socket activation"이 부팅을 가속하는 원리는?
A. 서비스 실제 시작을 lazy하게 만들어 의존성 체인 단축. 전통 모델: "SSH를 사용하려면 먼저 sshd를 시작해야 한다". systemd가 sshd.service를 시작 → 바이너리 실행 → sshd가 22 포트 listen → 부팅 진행. 클라이언트는 sshd가 "ready" 될 때까지 기다려야 함. Socket activation: systemd가 부팅 매우 일찍 sshd.socket unit을 활성화 — systemd 자체가 22 포트를 listen한다. sshd는 아직 시작 안 됨. 이제 부팅의 다른 부분이 병렬로 진행. 클라이언트가 22 포트에 연결하면 systemd가 sshd를 그 순간에 시작하고 이미 accept한 TCP 소켓을 파일 디스크립터로 상속시킨다. sshd는 마치 자기가 처음부터 listen했던 것처럼 동작. 이점: (1) 부팅 시간 단축 — 사용되지 않는 서비스는 전혀 시작 안 함, (2) Graceful restart — sshd 재시작 중에도 새 연결이 systemd 소켓에 대기, (3) 의존성 단순화 — "A 서비스가 B를 필요로 한다"의 경우 B가 fully ready될 때까지 기다릴 필요 없이 B의 소켓만 있으면 됨, (4) Inetd 부활 — on-demand 서비스 시작이 리소스 효율적. 이는 1990년대 inetd의 현대적 구현 — "매 요청마다 fork"가 아니라 "첫 요청에 시작해서 계속 실행". Docker, Kubernetes의 pod lifecycle도 이 아이디어에 영향 받음. systemd가 sd_listen_fds() API로 서비스에 소켓 전달. man 3 sd_listen_fds 참고.
이 글이 도움이 됐다면 다음 포스트도 확인해 보세요:
- "Linux 가상 메모리 Deep Dive" — 커널이 초기화하는 메모리 시스템.
- "Linux Scheduler Deep Dive" — systemd가 활용하는 cgroup과 CFS.
- "Hypervisors & Virtualization" — Firecracker의 125ms boot.
- "Container Internals" — namespaces, cgroups의 상세.
현재 단락 (1/799)
- **부팅은 계층적 부트스트래핑**. 각 단계가 더 많은 기능을 활성화한 후 다음 단계로 제어를 넘긴다: **Firmware (BIOS/UEFI) → Bootloader (GRU...