마이크로 리눅스 배포판 만들기 (2023)
(popovicu.com)- 리눅스 커널을 직접 빌드하고 최소한의 사용자 공간을 구성하여 ‘마이크로 리눅스 배포판’을 만드는 과정을 단계별로 설명
 - 운영체제 커널의 역할, 리눅스 배포판의 구성 요소, 그리고 커널과 사용자 공간의 관계를 기초부터 다룸
 - RISC-V 아키텍처(QEMU의 
riscv64 virt머신)를 예시로 사용하지만, x86 등 다른 아키텍처에도 동일한 원리 적용 가능 - 
init프로세스,initramfs, 그리고 Go로 작성한 간단한 셸을 포함한 직접 실행 가능한 최소 리눅스 환경을 구축 - 마지막으로 
u-root프로젝트를 이용해 실제 유용한 마이크로 배포판을 만드는 방법을 소개하며, 리눅스 시스템 구조 전반의 이해를 돕는 입문 가이드로 마무리 
운영체제 커널이란 무엇인가
- 커널은 하드웨어 자원 관리와 프로그램 실행 제어를 담당하는 운영체제의 핵심 구성 요소
- 단일 코어 환경에서도 여러 프로그램이 동시에 실행되는 것처럼 보이게 하는 멀티태스킹 관리 기능 제공
 
 - 커널은 입출력 장치 제어를 추상화하여 애플리케이션이 하드웨어 세부 주소나 레지스터 값을 직접 다루지 않도록 함
- 예를 들어, 프로그램은 단순히 “표준 출력에 메시지를 쓰라”고 요청하고, 커널이 실제 하드웨어와의 상호작용을 처리
 
 - 
파일시스템 인터페이스를 통해 고수준의 데이터 접근 방식을 제공
- 파일은 단순히 디스크 데이터가 아니라, 커널과 통신하는 논리적 인터페이스로 동작
 
 - 커널은 프로세스 간 격리 및 통신 모델을 제공하여, 각 애플리케이션이 독립적으로 실행되거나 협력할 수 있도록 함
 - Linux 커널은 오픈소스이며 다양한 아키텍처에서 동작 가능, 전 세계적으로 가장 널리 사용되는 커널 중 하나
 
리눅스 배포판이란 무엇인가
- 리눅스 커널만으로는 사용자가 웹 브라우저나 GUI 앱을 실행할 수 없으며, 커널 위에 여러 계층의 소프트웨어 인프라가 필요
 - 네트워크 설정, IP 할당, VPN 관리 등은 커널이 아닌 상위 사용자 공간 프로그램이 담당
 - 따라서 리눅스 배포판은 커널 + 사용자 공간 인프라의 조합으로 정의됨
 - 배포판은 커널이 제공하는 기본 기능 위에 패키지, 도구, 설정, 초기화 프로세스(init) 등을 포함
 - 배포판의 복잡도는 다양하며, Arch Linux처럼 최소 구성부터 Ubuntu처럼 사용자 친화적 구성까지 존재
 
커널 외부의 인프라: 사용자 공간과 init 프로세스
- 커널이 부팅을 마치면 가장 먼저 PID 1번 프로세스인 
init을 실행- 
init은 이후 모든 사용자 공간 프로세스의 조상으로, 시스템의 서비스와 도구를 순차적으로 실행 
 - 
 - 
init이 실행하는 각종 프로세스와 도구의 집합이 리눅스 배포판의 실질적 구성 요소 - 배포판이 복잡해질수록 불필요한 기능이 쌓여 “bloated” 하다는 비판을 받기도 함
 - 반대로, 커스텀 마이크로 배포판을 만들면 최소한의 기능만 포함된 경량 시스템을 구축 가능
 
RISC-V용 리눅스 커널 빌드
- 
x86환경에서 크로스 컴파일 도구체인을 이용해 RISC-V용 커널을 빌드- 
kernel.org에서linux-6.5.2.tar.xz소스 다운로드 후make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig실행 
 - 
 - 
menuconfig를 통해 커널 설정을 시각적으로 편집 가능 - 
make -j16으로 병렬 빌드 후arch/riscv/boot/Image생성 - QEMU에서 
qemu-system-riscv64 -machine virt -kernel arch/riscv/boot/Image로 부팅- 부팅 로그에서 SBI 레이어 감지, UART 초기화, printk 활성화 등의 메시지 확인 가능
 
 
첫 번째 장애: 루트 파일시스템 없음
- 커널 부팅 중 
VFS: Unable to mount root fs오류로 커널 패닉 발생- 원인: 루트 파일시스템(
initramfs)이 제공되지 않음 
 - 원인: 루트 파일시스템(
 - 파일시스템은 디스크뿐 아니라 RAM 기반(
initramfs) 으로도 구성 가능 - 
initramfs는cpio포맷으로 패키징되며, QEMU에서는-initrd옵션으로 로드 가능 
initramfs 구축과 “Hello world” 실행
- 최소 요구사항은 
/init바이너리 존재- 
init.c작성 후 정적 링크(-static)로 빌드 - 
cpio -o -H newc < file_list.txt > initramfs.cpio로 패키징 
 - 
 - QEMU 실행 시 “Hello world” 출력 후 
init종료로 다시 커널 패닉 발생- 해결: 
init이 종료되지 않도록 무한 루프 추가 
 - 해결: 
 
Go로 작성한 간단한 셸 추가
- 
init이fork와execl을 사용해/little_shell실행 - 
little_shell.go는 사용자 입력을 받아 명령을 에코 출력하는 단순 셸- 
GOOS=linux GOARCH=riscv64 go build little_shell.go로 RISC-V용 빌드 
 - 
 - 
init과little_shell모두 UART를 통해 출력 공유- 표준 입출력은 파일 핸들로 관리되며, 
fork시 상속됨 
 - 표준 입출력은 파일 핸들로 관리되며, 
 - 결과적으로 “Hello from init”과 셸 입력이 교차 출력되는 기초 리눅스 환경 완성
 
커널의 역할 정리
- 하드웨어 추상화: 사용자 프로그램은 UART나 디바이스 세부 정보를 몰라도 출력 가능
 - 
고수준 인터페이스 제공: 파일시스템을 통해 다른 바이너리(
little_shell) 접근 - 
프로세스 격리: 
init과 셸은 독립된 메모리 공간에서 실행 - 커널은 복잡한 하드웨어 위에서 안정적이고 이식성 높은 실행 기반을 제공
 
운영체제의 정의
- 커널만을 운영체제로 보기도 하고, 배포판 전체를 운영체제로 보기도 함
 - 중요한 것은 커널과 사용자 공간의 역할 경계와 상호작용 구조를 이해하는 것
 
u-root로 실제 유용한 마이크로 배포판 만들기
- 
u-root 프로젝트는 Go 기반 사용자 공간 도구 세트를 제공
- 
u-root는 리눅스 커널 위에서 실행되는 사용자 공간 부트로더 및 셸 환경 포함 
 - 
 - 설치 후 
GOOS=linux GOARCH=riscv64 u-root명령으로initramfs자동 생성- 
/tmp/initramfs.linux_riscv64.cpio파일을 QEMU에서 실행 가능 
 - 
 - 부팅 시 “Welcome to u-root!” 배너와 함께 기본 셸 프롬프트 제공
- 
ls,pwd,echo등 기본 명령어 지원, 탭 완성 기능 포함 
 - 
 
네트워크 연결 실습
- QEMU에 
virtio-net-device와virtio-rng-pci장치 추가- 
-device virtio-net-device,netdev=usernet -netdev user,id=usernet옵션 사용 
 - 
 - 
u-root의dhclient로 DHCP를 통해 IP 자동 할당- 예시: 
eth0에10.0.2.15/24할당 
 - 예시: 
 - 
wget http://google.com으로 외부 네트워크 접근 성공,index.html다운로드 확인 
패키지 관리자와 init의 중요성
- 일반 배포판은 패키지 관리자를 통해 소프트웨어를 동적으로 설치·업데이트
- 본 실습은 임베디드형 접근으로, 전체 이미지를 재빌드해야 함
 
 - 
init은 단순한 프로세스 실행기가 아니라 디바이스 초기화, 서비스 관리, 시스템 부팅 제어의 핵심 구성 요소- 
u-root의init소스코드를 통해 다양한 장치(/dev) 설정 과정을 확인 가능 
 - 
 
GitHub 저장소
- 본 가이드의 전체 코드와 예제는 popovicu/linux-micro-distro에서 제공
- 
initramfs이미지 빌드 및 실습 재현 가능 
 - 
 
Hacker News 의견
- 
몇 달째 마이크로 리눅스 배포판을 직접 만들고 있음
사용자 모드는 단일 정적 바이너리 하나로 구성되어 있고, confidential microVM 컨테이너를 지원하기 위한 몇 개의 파일만 있음
특히 initramfs 구조가 흥미로움. 커널이 cpio 아카이브를 풀고 tmpfs로 진입해 /init을 실행하는 과정이 마치 마법 같음
여러 개의 cpio 아카이브를 이어 붙일 수도 있고, 각각 압축 가능하며 순서대로 오버레이됨
이 단순하면서도 우아한 설계 덕분에 직접 언팩 코드를 작성하면서 많은 걸 배움 - 
최근에는 qemu가 주요 아키텍처에서 uftrace를 지원하기 시작했음
전문가들이 “이걸 어떻게 디버깅하지?”라고 물을 때 바로 그 답이 됨
관련 내용은 이 스레드에서 참고 가능함 - 
나도 비슷한 프로젝트를 진행 중임 — azathos
직접 만든 toy init, shell, 그리고 몇 가지 유틸리티를 포함하고 있음
디버깅용으로 GNU coreutils를 넣었고, 지금은 프레임버퍼에 윈도우를 그리는 기능에 집중하고 있음 - 
이 프로젝트 정말 멋짐. 98년에 플로피 기반 “배포판”을 만들어 Windows PC 이미징을 UDP 브로드캐스트로 하던 시절이 떠오름
“make bzimage”, init 스크립트 오류, 무한 재부팅… 추억이 많음
요즘 방식도 크게 다르지 않은 게 흥미로움. Raspberry Pi용으로 포팅하면 재미있고 교육적일 것 같음. 직접 시도해볼지도 모름- 나도 98년에 Mandrake Linux를 NetBIOS와 ISDN으로 설치하려다 체크섬 오류 때문에 수십 번 다시 시작했던 기억이 있음
결국 친구가 sftp 내용을 CD로 구워줘서 해결했는데, 그때는 2배속으로만 쓸 수 있었음 
 - 나도 98년에 Mandrake Linux를 NetBIOS와 ISDN으로 설치하려다 체크섬 오류 때문에 수십 번 다시 시작했던 기억이 있음
 - 
이걸 클라우드 이미지로 (예: Vultr, DigitalOcean) 실행하거나 GUI를 띄워 Firefox를 돌리는 게 얼마나 어려울지 궁금함
- 클라우드 이미지로 돌리는 건 비교적 쉬움. 커널의 기본 드라이버만 있으면 되고, 이미지를 설치하면 됨
다른 배포판으로 부팅한 뒤 kexec로 자신의 커널을 실행해 메모리 상에서 설치하는 방식도 가능함
실제 구현 예시는 nixos-anywhere를 참고할 수 있음 - 네트워크와 스토리지를 위한 virtio 드라이버를 포함한 이미지를 만들고 qcow2로 변환해 DigitalOcean 등에 등록하면 됨
생각보다 간단한 작업임 
 - 클라우드 이미지로 돌리는 건 비교적 쉬움. 커널의 기본 드라이버만 있으면 되고, 이미지를 설치하면 됨
 - 
이 프로젝트를 Raspberry Pi 대상으로 만든 버전이 있다면 정말 흥미로울 것 같음
 - 
왜 이런 걸 직접 만드는지 궁금했는데, 그냥 Gentoo로 리눅스를 탐구하는 건 안 되는지 생각해봄
- Gentoo는 “소스에서 빌드”하지만, 패키지 매니저가 대부분의 일을 대신해줌
사용자 공간 커스터마이징은 가능하지만, 리눅스 자체를 배우기엔 적합하지 않음
stage3 tarball만 봐도 이미 “미니 배포판” 수준임 
 - Gentoo는 “소스에서 빌드”하지만, 패키지 매니저가 대부분의 일을 대신해줌
 - 
학습용으로는 정말 좋고, 빠르게 완성하려면 buildroot가 좋은 선택지임
 - 
이 글 덕분에 정말 많은 걸 배움. 정보량이 풍부한 포스트라 감사함