갑자기 부팅 과정…?
- 이 전까지는 기본적인 스토리지 세팅 과정이었음
- 근데 갑자기 리눅스가 부팅이 안되면 어떡함
- 그래서 다시 한 번 뒤로 돌아가서, 부팅 과정부터 보기로 함
전체적인 부팅 과정
- 전원 버튼 누름
- 펌웨어 켜짐
- 부트로더 불러오기
- 커널 + initramfs 파일 읽기
- systemd 켜기
- 사용 가능
- 그래서, 이번에는 펌웨어/부트로더/커널/systemd 에 대해 하나씩 파헤쳐 볼 예정임
1. 펌웨어
개요
- 우리는 흔히 기계를 하드웨어와 소프트웨어로 나누어서 생각함
- 하드웨어 : 실제 물체. 기계 장비. CPU, RAM, GPU 등등
- 소프트웨어 : 하드웨어를 기동한 뒤에 사용할 수 있는 “수정 가능한” 것들. 프로그램이라 부르는 것들
- 그럼 펌웨어는 무엇인가? ⇒ 하드웨어와 소프트웨어를 잇는 다리 역할을 하는 “특수한” 소프트웨어
- 참고 : firm 이 단단하다라는 뜻임. 변경이 어렵기 때문에 소프트웨어보단 단단해서 붙은 듯
소프트웨어와의 좀 더 큰 차이점
- 소프트웨어는 기본적으로 “RAM”에서 CPU 자원을 받아다가 작동함
- 그런데, 펌웨어는 나중에 변경되거나 지워지면 안되기 때문에 제조 단계에서 ROM에 작성됨
- 그렇기 때문에 펌웨어는 “ROM”에서 작동함
- 그리고 당연하지만 OS랑도 다름. OS는 펌웨어 위에 얹어져서 쓰임.
작동 순서
- 하드웨어 초기화 : 처음에 전원이 들어오면 펌웨어가 물리적으로 연결된 CPU, GPU, RAM 등등을 인식하고 정상적으로 작동할 수 있도록 설정함
- 운영체제 준비 : 운영체제가 들어있는 장치를 우선적으로 판단하고, 운영체제를 메모리로 올림
종류
- BIOS 와 UEFI가 있음.
- 두 개는 차이점이 꽤 있지만, 그 중 가장 중요한 차이점은 OS 부팅을 위한 방법이 다르다는 점
| BIOS | UEFI | |
|---|---|---|
| 부팅 방법 | 디스크의 0번 섹터 (MBR)을 읽음 | 특정 파일을 읽음 |
| 디스크 레이블 타입 | MBR | GPT |
| 디스크 제한 | 2TB | 사실상 없음 |
| 안전 모드 | 없음 | 있음 |
디스크 레이블 타입
- 디스크의 파티션 구조와 정보를 정의하는 방식
- 대표적으로 MBR과 GPT가 있으나, 간혹 특정 시스템의 경우 자체적인 타입이 있는 경우가 있으므로 확인 필요함
MBR
- Master Boot Record
- 디스크의 첫 번째 섹터 (512 바이트)에 파티션 정보와 부트 로더를 기록함
- 그래서 앞에 섹터 고장나면 못씀
- BIOS 기반 시스템에서 쓰던 예전 방식
- 최대 4개의 Primary Partition을 둘 수 있음
- 디스크 최대 크기는 2TB
GPT
- GUID Partition Table
- DOS라고도 부름
- UEFI 표준에 맞춰 설계됨
- 각각의 파티션에 아이디(GUID)를 부여해 구분함
- 파티션 테이블을 앞이랑 뒤에 이중으로 저장함
- 게다가 CRC32 체크섬이 포함되어서 무결성 검사도 함
- 파티션 수 제한은 128개
- 디스크 최대 크기는 9.2ZB
UEFI 의 독특한 방식
-
UEFI 의 경우 부팅하기 위한 “특정 파일을 읽기” 때문에, 그 파일을 저장하고 불러오기 위한 파티션이 필요함
-
그걸 ESP (EFI System Partition) 이라고 함
-
ESP는 파일 시스템으로 FAT32 방식을 사용하며, 100~600 MB 정도의 용량을 가짐
-
어디에 있는지 볼 수 있음.
-
(CentOS 기준임)

- 여기 보면 첫 번째 디스크 (sda) 가 파티션 세 개로 나뉜 것을 볼 수 있음
- 세 개의 파티션 중에 sda1을 보면, 마운트 포인트가 /boot/efi 임
- 이게 ESP임.

- 여기 보면 .efi 로 끝나는 파일들이 있음. 이 파일들을 사용해서 시스템을 부팅하는 것.
안전 모드?
- UEFI에는 안전 모드가 있음.
- 서명된 안전한 부트로더만 가져와서 실행시키는 방법임.
- 그럼 그 서명된 애들을 누가 알아볼까?
- OS마다 다른데, RedHat 계열의 경우 shim 을 씀
- 왜 쓰는가? ⇒ 좀 있다가 나올 부트로더인 grub 은 마이크로소프트에서 인증을 안해줬음
- 그래서 대신에 인증해준 shim을 한번 거쳐서 부팅함
- 따라서 진짜 안전한 애들만 부팅이 됨
2. 부트로더 불러오기
개요
- 펌웨어가 하드웨어와 소프트웨어 사이를 잇는 다리라면,
- 부트로더는 펌웨어와 커널 사이를 잇는 다리 역할을 함.
- 커널이 뭔지는 조금 뒤에 설명함
작동 순서
- 커널 선택
- initramfs 선택
- 커널 파라미터 전달
- 커널 실행
종류
-
부트로더는 종류가 다양한데, 현재 리눅스 계열에서 가장 널리 쓰이는 부트로더는 grub 이다.
- 여기서 실습하는 CentOS 또한 grub 을 사용한다. 그래서 grub에 대한 설명을 포함할 것이다.
-
U-Boot 라고 임베디드 쪽에서 많이 쓰는 부트로더가 있다고 한다.
GRUB
- Grand Unified Bootloader.
- 위에서 말했듯이, 현재 리눅스 계열에서 가장 많이 쓰이는 부트로더다.
구조
-
자동생성이 되는 파일이 있고, 수정 가능한 파일이 있음.
-
수정 가능한 파일은 다시 두 가지로 나뉨
- 전역 설정 (/etc/default/grub)
- 스크립트 조각들 (/etc/grub.d/ 하위의 파일들)
-
전역 설정 파일 예시

-
스크립트 조각들 예시

-
수정 가능한 파일들은 grub2-mkconfig 명령을 통해 합쳐져서 자동생성이 되는 파일로 만들어진다.
-
참고 : 스크립트 조각들의 경우, 오름차순으로 명령을 합친다. (00 → 41)
-
자동 생성된 파일은 펌웨어의 종류에 따라 위치가 달라진다.
- BIOS : /boot/grub2/grub.cfg
- UEFI : /boot/efi/EFI/<OS_또는_Vendor>/grub.cfg
GRUB는 커널에 과연 무엇을 넘겼는가?
- 아래의 명령어를 실행하면 확인할 수 있음
cat /proc/cmdline- 출력 예시

- 부팅해야되는 이미지는 여기있고… 루트는 어디고… 어쩌구저쩌구.
3. 커널 + initramfs 불러오기
커널?
- 운영체제 중에서 메모리에 “항시” 올라가 있는 핵심 부분을 말함
- CPU, GPU, RAM 같은 자원과 응용 프로그램을 잇는 역할을 함
- 메모리, 프로세스 관리 + 서비스 요청 수신의 역할도 함
initramfs?
- Initial RAM File System.
- 커널이 올라오고 나서 실제 파일 시스템을 로드하기 전까지 잠깐동안 초기 환경 세팅을 제공하는 임시 메모리 기반 파일 시스템임
- 예전에 있던 initrd (Initial RAM Disk) 를 대체함
- 초소형 리눅스임. 안에 init 이라는 최초 실행 파일이 있음
- 그래서 initramfs 안에서는 init 이 PID 1번임.
- 초기 드라이버를 로드함
- 실제 파일 시스템을 찾고 마운트도 함 (이게 약간 주 목적임)
- 이름에서 보면 알겠지만 디스크가 아니고 RAM에 들어오는거라 되게 빠름
커널이 있는데 initramfs가 왜 필요함?
- 중요한게 있는데, 커널은 켜졌을 때 디스크의 존재 자체를 모름
- 파일 시스템 드라이버도 모름
- 그래서 initramfs가 커널 이후에 올라와서 드라이버들을 로드해주는거임
- 결과적으로 보면 커널은 루트 디렉토리가 마운팅되기 전까지 initramfs한테 의존하고 있음
initramfs는 어떻게 수정하나요
- 이전 글들에서 자주 봤었던 dracut 이라는 프로그램이 그 역할을 함.
- dracut은 기본적으로 아래의 파일들을 참고함
| 분류 | 파일 |
|---|---|
| 루트 파일 시스템 | /etc/fstab, /proc/cmdline |
| RAID | /etc/mdadm.conf |
| LVM | /etc/lvm/lvm.conf |
| 암호화 | /etc/crypttab |
| dracut 설정 | /etc/dracut.conf, /etc/dracut.conf.d/* |
| 드라이버 | /sys, /lib/modules/ |
| udev | /etc/udev/rules.d/* |
- 참고 : 커널 업데이트 하면 dracut으로 그 커널 버전에 맞는 initramfs 새로 만들어야 함.
# $(uname -r) => 커널 버전
dracut -H -f /boot/initramfs-$(uname -r).img $(uname -r)4. systemd
개요
- initramfs를 통해 루트 시스템을 마운팅하고 난 뒤에 최초로 실행되는 프로그램이 systemd(/sbin/init) 임

하는 일
-
고아 프로세스 처리 : 부모가 사라진 프로세스를 종료 처리
-
SIGCHLD(자식 프로세스의 종료 시) 처리 : 좀비 프로세스 처리
-
시스템 종료
-
서비스 생명주기 관리
-
참고 : systemd가 비정상 종료되면 커널 패닉 or 시스템 정지됨
-
참고 2 : 예전에 쓰던 SysVInit 은 순차적 스크립트 실행이었는데, 그걸 보완하기 위해 나옴
- 의존성 기반 병렬 실행이 가능하고, 상태 추적도 가능함
타겟
- 원하는 특정 상태에 도달하기 위해 실행해야 하는 systemd 유닛 (서비스) 집합
- .target 이라는 확장자로 존재함
- 위치 : /lib/systemd/system/ 그리고 /etc/systemd/system/

- 되게 많음
- 이 중에서 자주 쓰이는 타겟은 다음 표에 있음
- 표 출처
| Target | 의미 |
|---|---|
sysinit.target | 파일시스템 마운트, swap 설정 등 초기화 |
basic.target | 기본 서비스 준비 (dbus, 로그 등) |
network.target | 네트워크 장치만 감지됨 (연결 여부는 미확정) |
network-online.target | 네트워크가 완전히 연결됨 (DNS, 외부 통신 가능) |
multi-user.target | 텍스트 기반 로그인 환경 완성 (SSH, DB 등 대부분의 서버 서비스 실행) |
graphical.target | GUI 환경까지 실행 (X11, Wayland 포함) |
default.target | 시스템이 부팅 시 기본적으로 도달하는 상태 (보통 multi-user 또는 graphical) |
- 또한 실제 서비스 예시도 있음. (출처 위 표와 같음)
| Target | 실제 사용하는 서비스 예시 |
|---|---|
network.target | DHCP 클라이언트, UFW 방화벽 |
network-online.target | NFS 클라이언트, Airflow, Kafka consumer |
multi-user.target | Nginx, PostgreSQL, Docker, sshd |
graphical.target | gdm, sddm, GNOME, KDE |
sockets.target | docker.socket, cups.socket |
timers.target | apt-daily.timer, logrotate.timer |
서비스를 타겟에 등록하는 방법
0. 예시 시나리오
- 현재 내가 만든 프로그램이 /usr/local/bin/my_server 안에 있음
- 이 프로그램은 서버 프로그램이라서, multi-user.target에 등록시키는게 적합하다고 판단했다는 상황
1. 서비스부터 만들기
- 프로그램을 서비스로 만들려면 /etc/systemd/system/ 폴더 안에 서비스 유닛(파일)을 만들어야 함
- 예시
# /etc/systemd/system/my_server.service
[Unit] # 조건, 순서
Description=My First Server
After=network.target # 네트워크 타겟이 충족된 이후에 실행 (느슨함)
[Service] # 실행 방법
ExecStart=/usr/local/bin/my_server # 실행 파일 위치
Restart=always # 종료되었을 때 재시작 여부
[Install] # 어느 Target에 묶일지
WantedBy=multi-user.target # 이 타겟 상태가 된 후에 실행함. 실패해도 이후를 진행함- 참고 : 만약에, “무조건 A 서비스가 실행된 후에 B 서비스가 실행되어야만 해!” 라고 한다면 아래의 설정을 함
# /etc/systemd/system/B.service
[Unit]
Requires=A.service- 이 경우, A가 실행되지 않았으면 아예 target도 실패처리되기 때문에 부팅에 문제가 생길 수 있음 주의
2. systemd에게 추가된 서비스를 인식시키기
systemctl daemon-reload # 변경 된 unit 파일들을 다시 읽어서 systemd에 반영함
systemctl enable <서비스유닛> # 자동 부팅 활성화 + 지정 타겟.target.wants 디렉토리에 심볼릭 링크 생성- 예시
systemctl daemon-reload
systemctl enable my_server.service- 참고 : 만약 재시작 하기 전에 지금 당장 키고싶으면 아래의 명령어 실행
systemctl start <서비스유닛>- 예시
systemctl start my_server.service특정 타겟 서비스들 확인하기
systemctl list-dependencies <타겟파일명>- 예시
systemctl list-dependencies multi-user.target
유용한 타겟 관련 명령어들
- 부팅을 할 때 최종적으로 도달하려는 타겟(default.target)을 확인하는 명령어
systemctl get-default
- default.target을 다른걸로 바꾸는 명령어
- 참고 : default.target은 특정 파일이 아니고 링크기 때문에, 다른 타겟 파일로 링크를 옮기는 것 뿐임
systemctl set-default <변경할_타겟파일명>- 예시
systemctl set-default rescue.target- 부팅 이후에 어떤 유닛이 켜지지 못했는지 확인하는 명령어
systemctl list-units --failed
- 참고 : 일회성으로만 (다음 부팅만) 타겟을 바꾸고 싶으면 GRUB에서 바꾸는게 맞음
다음에 할 것
- 일부러 부팅이 안되게끔 하고 고치는 실습 해보기