커널 개발 환경 설정

Linux 커널 개발을 위한 개발 환경 구축 가이드: 필수 도구 설치, 에디터 설정, QEMU/KVM 가상 환경, 크로스 컴파일, GDB/KGDB 디버거 설정까지 완벽 정리.

다음 단계: 이 문서로 환경을 갖춘 뒤 커널 빌드 시스템커널 모듈로 진행하세요. 개발 환경은 모든 커널 작업의 기반이므로 이 문서가 학습의 출발점입니다.
일상 비유: 이 주제는 작업장 공구 세팅과 비슷합니다. 목공 작업에 톱, 망치, 드릴이 필요하듯이, 커널 개발에는 컴파일러, 디버거, 에디터가 필수입니다. 도구를 제대로 갖추면 작업 효율이 크게 높아집니다.

핵심 요약

  • 필수 도구 — gcc, make, git, flex, bison, libelf-dev 등 빌드에 필수적인 패키지를 먼저 설치합니다.
  • 개발 보조 도구 — ctags, cscope, clangd 등으로 코드 탐색과 자동완성을 강화합니다.
  • 가상 환경 — QEMU/KVM으로 안전하게 커널을 테스트하고 디버깅합니다.
  • 크로스 컴파일 — ARM, ARM64, RISC-V 등 다른 아키텍처용 커널을 빌드합니다.
  • 디버거 설정 — GDB/KGDB로 커널 소스 레벨 디버깅을 수행합니다.

단계별 이해

  1. 도구 설치
    배포판에 맞는 패키지 관리자로 필수 도구를 설치합니다.
  2. 에디터 구성
    선호하는 에디터에 코드 탐색 도구를 연동합니다.
  3. 가상 환경 준비
    QEMU/KVM으로 테스트용 가상 머신을 구성합니다.
  4. 첫 빌드 실행
    간단한 설정으로 커널을 빌드하고 부팅 테스트합니다.
  5. 디버거 연동
    GDB와 QEMU를 연결하여 커널 디버깅 환경을 완성합니다.

환경 설계 원칙

커널 개발 환경은 단순 설치보다 재현성, 격리, 검증 가능성이 중요합니다. 처음 한 번만 잘 구성하면 이후 실험 속도와 안정성이 크게 올라갑니다.

원칙 설명 실무 권장
재현성 같은 입력이면 같은 빌드 결과가 나와야 함 툴 버전 고정, 설정 파일(.config) 보관, 빌드 로그 아카이브
격리 호스트 시스템과 테스트 환경 분리 QEMU/KVM 기본, 실제 장비는 후반 검증 단계에서만 사용
검증 가능성 문제 발생 시 원인 추적이 가능해야 함 CONFIG_DEBUG_INFO, FRAME_POINTER, 로그 수집 자동화
점진적 확장 필수 도구부터 시작해 점진적으로 추가 필수(빌드) → 권장(탐색/가상화) → 선택(분석/자동화)
권장 시작 프로필: 초반에는 단일 아키텍처(x86_64) + QEMU + GDB 조합으로 시작하세요. 크로스 컴파일과 고급 분석 도구는 기본 루프(수정→빌드→부팅→디버깅)가 안정화된 뒤 추가하는 편이 전체 학습 속도가 빠릅니다.

개발 도구 의존성 로드맵

커널 개발 환경은 여러 도구들이 계층적으로 연결된 생태계입니다. 아래 다이어그램은 각 도구의 역할과 의존 관계를 보여주며, 환경 구축 순서를 안내합니다.

커널 개발 도구 의존성 로드맵 Layer 0: 기본 시스템 Linux 배포판 패키지 관리자 Bash/Shell Python3 Perl Layer 1: 핵심 빌드 도구 (필수) GCC gcc-12+ GNU Make make 3.82+ binutils ld, as, ar flex/bison 파서 생성 libelf-dev BTF/BPF libssl-dev 모듈 서명 Git 버전 관리 Layer 2A: 코드 탐색 (권장) ctags 심볼 인덱싱 cscope 참조 추적 clangd LSP 서버 ripgrep Layer 2B: 가상화 (권장) QEMU 에뮬레이터 KVM 하드웨어 가속 virtme-ng 빠른 테스트 busybox Layer 3: 디버깅 & 분석 (선택) GDB 소스 디버깅 crash vmcore 분석 perf 프로파일링 trace-cmd ftrace UI bpftrace 동적 추적 sparse 정적 분석 coccinelle 코드 변환 checkpatch 스타일 검사 pahole Layer 4: 고급 도구 (전문가) 크로스 컴파일 툴체인 ccache / distcc clang / LLVM ktest / kernelci syzkaller (fuzzing) b4 (패치 관리)
그림: 커널 개발 도구 의존성 로드맵 - 계층별 설치 순서
💡

환경 구축 권장 순서:

  1. 최소 환경 (1~2시간): Layer 0-1 + QEMU → 간단한 커널 빌드/부팅 가능
  2. 기본 개발 (반나절): + Layer 2A/2B → 코드 탐색 및 가상머신 테스트
  3. 완전한 환경 (1일): + Layer 3 → 디버깅 및 분석까지 모든 작업 가능
  4. 전문가 환경 (지속): + Layer 4 → 다중 아키텍처 개발 및 자동화

디스크 공간: 커널 소스 3GB + 빌드 결과 10GB + 가상머신 이미지 5GB = 최소 20GB 여유 필요

필수 개발 도구 설치

Linux 커널 빌드를 위해서는 컴파일러, 빌드 시스템, 버전 관리 시스템, 그리고 다양한 유틸리티가 필요합니다. 배포판별로 패키지 이름이 다를 수 있으므로 각 배포판에 맞는 명령어를 사용하세요.

Ubuntu / Debian 계열

# 필수 빌드 도구
sudo apt update
sudo apt install -y build-essential \
  gcc make git \
  flex bison \
  libelf-dev libssl-dev \
  bc libncurses-dev \
  cpio rsync

# 커널 문서 빌드 도구 (선택)
sudo apt install -y python3-sphinx \
  texlive-latex-base texlive-latex-extra

# 추가 유틸리티
sudo apt install -y kmod dwarves \
  sparse ccache
패키지 설명:
  • build-essential: gcc, g++, make 등 기본 빌드 도구 모음
  • flex, bison: 파서 생성기 (커널 빌드 스크립트에서 사용)
  • libelf-dev: BPF, eBPF 프로그램 빌드에 필요
  • libssl-dev: 서명된 커널 모듈 빌드에 필요
  • bc: 커널 빌드 스크립트의 계산기
  • libncurses-dev: menuconfig TUI에 필요
  • dwarves: pahole 등 DWARF 디버깅 정보 분석 도구
  • sparse: 정적 분석 도구
  • ccache: 컴파일러 캐시로 재빌드 속도 향상

설치 직후 검증 명령

패키지 설치가 끝나면 바로 아래 명령으로 도구 상태를 확인하세요. 설치 자체보다 실행 가능한 상태를 검증하는 과정이 중요합니다.

# 필수 도구 버전 확인
gcc --version | head -1
make --version | head -1
git --version
ld --version | head -1
flex --version
bison --version | head -1

# 커널 빌드 관련 라이브러리 존재 확인
pkg-config --modversion libelf
openssl version

# 커널 소스에서 최소 빌드 검증
make mrproper
make defconfig
make -j$(nproc) bzImage
검증 포인트: make defconfig가 실패하면 ncurses, flex, bison, bc 계열 의존성이 누락됐을 가능성이 큽니다. bzImage 빌드가 실패하면 컴파일러/링커/헤더 버전 조합을 우선 확인하세요.

버전 관리 정책

커널 개발에서는 "최신 버전"보다 "팀 전체에서 동일한 조합"이 더 중요할 때가 많습니다. 도구 버전을 팀 기준으로 고정하면 재현 불가 버그를 크게 줄일 수 있습니다.

Fedora / RHEL / CentOS 계열

# 필수 빌드 도구
sudo dnf groupinstall -y "Development Tools"
sudo dnf install -y gcc make git \
  flex bison \
  elfutils-libelf-devel openssl-devel \
  bc ncurses-devel \
  cpio rsync

# 추가 유틸리티
sudo dnf install -y kmod dwarves \
  sparse ccache

Arch Linux

# 필수 빌드 도구
sudo pacman -S --needed base-devel \
  gcc make git \
  flex bison \
  libelf openssl \
  bc ncurses \
  cpio rsync

# 추가 유틸리티
sudo pacman -S kmod pahole \
  sparse ccache

LLVM/Clang 대체 툴체인

리눅스 커널은 GCC 외에도 LLVM/Clang으로 공식 빌드를 지원합니다. Clang은 더 상세한 경고 메시지, CFI(Control Flow Integrity), LTO(Link Time Optimization) 등 GCC에 없는 보안/최적화 기능을 제공합니다.

LLVM/Clang 설치

# Ubuntu/Debian (LLVM 18 권장)
sudo apt install -y clang-18 lld-18 llvm-18

# Fedora
sudo dnf install -y clang lld llvm

# Arch Linux
sudo pacman -S clang lld llvm

Clang으로 커널 빌드

# 기본 Clang 빌드
make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm \
     STRIP=llvm-strip OBJCOPY=llvm-objcopy \
     OBJDUMP=llvm-objdump READELF=llvm-readelf \
     HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar \
     defconfig

# 간편한 방법: LLVM=1 (모든 도구를 LLVM으로)
make LLVM=1 defconfig
make LLVM=1 -j$(nproc)

# 특정 LLVM 버전 지정
make LLVM=-18 defconfig
make LLVM=-18 -j$(nproc)

# Clang으로 크로스 컴파일 (단일 바이너리로 모든 아키텍처)
make LLVM=1 ARCH=arm64 defconfig
make LLVM=1 ARCH=arm64 -j$(nproc)

Clang 전용 기능

기능 설정 옵션 설명
CFI CONFIG_CFI_CLANG 간접 호출 대상 검증, 코드 재사용 공격 방어
LTO (Thin) CONFIG_LTO_CLANG_THIN 링크 타임 최적화 (전체 프로그램 최적화)
Shadow Call Stack CONFIG_SHADOW_CALL_STACK ROP 공격 방어 (ARM64)
KCFI CONFIG_CFI_CLANG 커널 전용 CFI 구현
Auto-init CONFIG_INIT_STACK_ALL_ZERO 스택 변수 자동 초기화 (정보 유출 방지)

GCC vs Clang 비교

항목 GCC Clang/LLVM
역사 커널 공식 기본 컴파일러 4.x부터 공식 지원, Android 커널 기본
에러 메시지 간결 상세하고 컬러풀, 제안 포함
경고 수준 보수적 더 많은 잠재 문제 감지
크로스 컴파일 아키텍처별 별도 툴체인 단일 바이너리로 모든 아키텍처
LTO 지원 (느림) ThinLTO로 빠르고 효율적
보안 기능 기본 CFI, Shadow Call Stack 등 추가
빌드 속도 보통 비슷하거나 약간 빠름
플러그인 GCC 플러그인 지원 미지원 (대안 기능 제공)
실무 권장: 개발 중에는 GCC와 Clang 모두로 빌드하면 더 많은 경고를 잡을 수 있습니다. GCC에서 통과하는 코드가 Clang에서 경고를 발생시키는 경우(또는 그 반대)가 적지 않습니다. CI에서 양쪽 컴파일러 모두 테스트하는 것이 이상적입니다.
# 양쪽 컴파일러로 빌드 테스트
# GCC 빌드
make O=build-gcc defconfig
make O=build-gcc -j$(nproc) 2>&1 | tee gcc-warnings.log

# Clang 빌드
make O=build-clang LLVM=1 defconfig
make O=build-clang LLVM=1 -j$(nproc) 2>&1 | tee clang-warnings.log

# 경고 비교
diff <(grep "warning:" gcc-warnings.log | sort) \
     <(grep "warning:" clang-warnings.log | sort)

코드 탐색 도구

대규모 커널 소스 코드를 효율적으로 탐색하려면 인덱싱 도구가 필수입니다. ctags, cscope, clangd 등을 사용하면 함수 정의 이동, 호출 계층 추적, 심볼 검색이 빠르게 가능합니다.

ctags & cscope

# Ubuntu/Debian
sudo apt install -y universal-ctags cscope

# Fedora/RHEL
sudo dnf install -y ctags cscope

# Arch Linux
sudo pacman -S ctags cscope

# 커널 소스 루트에서 인덱스 생성
cd /path/to/linux
make tags        # ctags 생성
make cscope      # cscope 생성

clangd (Language Server Protocol)

clangd는 LSP 프로토콜을 지원하는 현대적인 코드 인텔리전스 도구로, 자동완성, 정의 이동, 참조 찾기, 리팩토링 등을 제공합니다.

# Ubuntu/Debian (LLVM 14 이상 권장)
sudo apt install -y clangd-14

# Fedora/RHEL
sudo dnf install -y clang-tools-extra

# Arch Linux
sudo pacman -S clang

# 커널 소스에서 compile_commands.json 생성
cd /path/to/linux
make defconfig
./scripts/clang-tools/gen_compile_commands.py
Tip: compile_commands.json은 clangd가 프로젝트 구조를 이해하는 데 사용하는 컴파일 데이터베이스입니다. 커널 설정을 변경할 때마다 재생성하세요.

커널 소스는 변경량이 크기 때문에 인덱스를 한 번만 생성하면 금방 오래된 정보가 됩니다. 다음 규칙으로 관리하면 탐색 정확도가 유지됩니다.

  1. 브랜치 전환 직후: make tags cscope 재생성
  2. .config 변경 후: gen_compile_commands.py 재실행
  3. 대규모 리베이스 후: 인덱스 파일 삭제 후 전체 재생성
# 안전한 인덱스 재생성 루틴
rm -f tags cscope.out cscope.in.out cscope.po.out compile_commands.json
make tags cscope
./scripts/clang-tools/gen_compile_commands.py
검색 습관: 심볼 점프 전에 rg로 먼저 텍스트 맥락을 보고, 이후 ctags/cscope/clangd로 정확한 정의로 이동하면 탐색 속도가 안정적입니다.

에디터 설정

Vim

Vim은 커널 개발자들 사이에서 가장 인기 있는 에디터 중 하나입니다. cscope와 ctags를 통합하여 강력한 코드 탐색 환경을 구축할 수 있습니다.

.vimrc 설정 예제

" ~/.vimrc

" ===== 기본 설정 =====
set number              " 행번호 표시
set relativenumber      " 상대 행번호
set tabstop=8          " 탭 너비 (커널 코딩 스타일)
set shiftwidth=8       " 인덴트 너비
set noexpandtab         " 탭을 스페이스로 변환 안 함
set autoindent
set smartindent
set hlsearch            " 검색 결과 하이라이트
set incsearch           " 증분 검색

" ===== ctags 설정 =====
set tags=./tags,tags;   " 상위 디렉토리까지 tags 파일 검색

" ===== cscope 설정 =====
if has("cscope")
    set csprg=/usr/bin/cscope
    set csto=0
    set cst
    set nocsverb
    " cscope.out 자동 로드
    if filereadable("cscope.out")
        cs add cscope.out
    endif
    set csverb
endif

" ===== cscope 키 바인딩 =====
nmap <C-\>s :cs find s <C-R>=expand("<cword>")<CR><CR>  " 심볼 찾기
nmap <C-\>g :cs find g <C-R>=expand("<cword>")<CR><CR>  " 정의 찾기
nmap <C-\>c :cs find c <C-R>=expand("<cword>")<CR><CR>  " 호출 찾기
nmap <C-\>t :cs find t <C-R>=expand("<cword>")<CR><CR>  " 텍스트 찾기

" ===== 커널 코딩 스타일 =====
autocmd FileType c,cpp setlocal cindent cinoptions=:0,l1,g0,(0,W4

Visual Studio Code

VS Code는 풍부한 확장 생태계와 직관적인 UI를 제공합니다. clangd 확장을 사용하면 강력한 인텔리센스를 활용할 수 있습니다.

필수 확장 설치

settings.json 설정

{
    "editor.tabSize": 8,
    "editor.insertSpaces": false,
    "editor.detectIndentation": false,
    "editor.rulers": [80, 100],
    "files.trimTrailingWhitespace": true,
    "files.insertFinalNewline": true,

    // clangd 설정
    "clangd.path": "/usr/bin/clangd-14",
    "clangd.arguments": [
        "--background-index",
        "--clang-tidy",
        "--completion-style=detailed",
        "--header-insertion=never"
    ],

    // C/C++ 확장 IntelliSense 비활성화 (clangd와 충돌 방지)
    "C_Cpp.intelliSenseEngine": "Disabled"
}

Emacs

Emacs는 오랜 역사를 가진 강력한 에디터로, 커널 개발에 최적화된 다양한 모드를 제공합니다.

;; ~/.emacs.d/init.el

;; ===== 기본 설정 =====
(setq-default indent-tabs-mode t)
(setq-default tab-width 8)
(setq c-basic-offset 8)

;; ===== 커널 코딩 스타일 =====
(defun linux-kernel-coding-style ()
  (setq c-indent-level 8
        c-brace-imaginary-offset 0
        c-brace-offset -8
        c-argdecl-indent 8
        c-label-offset -8
        c-continued-statement-offset 8
        indent-tabs-mode t
        tab-width 8))

(add-hook 'c-mode-hook 'linux-kernel-coding-style)

;; ===== cscope 설정 =====
(require 'xcscope)
(cscope-setup)

;; ===== lsp-mode (clangd) =====
(use-package lsp-mode
  :hook (c-mode . lsp-deferred)
  :commands (lsp lsp-deferred)
  :config
  (setq lsp-clients-clangd-args
        '("--background-index"
          "--clang-tidy"
          "--completion-style=detailed")))

Neovim (현대적 Vim)

Neovim은 Vim의 현대적 포크로, 내장 LSP 클라이언트, Lua 설정, treesitter 구문 강조 등 커널 개발에 유용한 기능을 기본 제공합니다.

# Ubuntu/Debian (최신 버전)
sudo add-apt-repository ppa:neovim-ppa/unstable
sudo apt install neovim

# Fedora
sudo dnf install neovim

# Arch Linux
sudo pacman -S neovim
-- ~/.config/nvim/init.lua

-- ===== 기본 설정 (커널 코딩 스타일) =====
vim.opt.tabstop = 8
vim.opt.shiftwidth = 8
vim.opt.expandtab = false
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.colorcolumn = "80,100"
vim.opt.signcolumn = "yes"

-- ===== 내장 LSP 설정 (clangd) =====
vim.lsp.start({
  name = "clangd",
  cmd = { "clangd",
    "--background-index",
    "--clang-tidy",
    "--completion-style=detailed",
    "--header-insertion=never",
    "-j=4",
  },
  root_dir = vim.fs.dirname(
    vim.fs.find({"compile_commands.json", "Makefile"}, { upward = true })[1]
  ),
})

-- ===== LSP 키 바인딩 =====
vim.keymap.set("n", "gd", vim.lsp.buf.definition)
vim.keymap.set("n", "gr", vim.lsp.buf.references)
vim.keymap.set("n", "K", vim.lsp.buf.hover)
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename)

-- ===== ctags/cscope 호환 =====
vim.opt.tags = "./tags,tags;"

-- ===== 진단 표시 =====
vim.diagnostic.config({
  virtual_text = true,
  signs = true,
  underline = true,
})
에디터 선택 가이드:
  • Vim: 가벼움, 어디서나 사용 가능, 서버 환경 기본 탑재
  • Neovim: Lua 설정, 내장 LSP, treesitter 기본 제공, 현대적 플러그인 생태계
  • VS Code: GUI 기반, 직관적 디버깅, Remote-SSH로 원격 개발 우수
  • Emacs: 강력한 커스터마이징, Org-mode, TRAMP(원격 편집)

핵심은 에디터 자체보다 clangd + compile_commands.json 조합입니다. 이것만 설정되면 어떤 에디터든 동일한 코드 인텔리전스를 제공합니다.

QEMU/KVM 가상 환경 설정

실제 하드웨어에서 커널을 테스트하는 것은 위험하므로, QEMU/KVM 가상 머신을 사용하면 안전하게 커널을 부팅하고 디버깅할 수 있습니다.

QEMU 설치

# Ubuntu/Debian
sudo apt install -y qemu-system-x86 qemu-system-arm qemu-system-aarch64 \
  qemu-utils libvirt-daemon-system virt-manager

# Fedora/RHEL
sudo dnf install -y qemu-kvm qemu-img libvirt virt-manager

# Arch Linux
sudo pacman -S qemu qemu-arch-extra libvirt virt-manager

# 현재 사용자를 libvirt, kvm 그룹에 추가
sudo usermod -aG libvirt,kvm $USER
newgrp libvirt

최소 루트 파일시스템 생성

# Buildroot로 간단한 rootfs 생성
git clone https://git.buildroot.net/buildroot
cd buildroot
make qemu_x86_64_defconfig
make menuconfig
# Filesystem images -> ext4 선택
make -j$(nproc)

# 생성된 이미지: output/images/rootfs.ext4
Tip: Buildroot 대신 Debian debootstrap, Alpine Linux minirootfs, BusyBox 기반 커스텀 이미지 등을 사용할 수도 있습니다.

QEMU로 커널 부팅

# 커널 빌드
cd /path/to/linux
make x86_64_defconfig
# 디버깅을 위해 CONFIG_DEBUG_INFO, CONFIG_GDB_SCRIPTS 활성화
./scripts/config --enable DEBUG_INFO
./scripts/config --enable GDB_SCRIPTS
./scripts/config --enable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
make -j$(nproc)

# QEMU로 부팅 (직렬 콘솔 출력)
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -hda /path/to/rootfs.ext4 \
  -append "root=/dev/sda rw console=ttyS0" \
  -nographic \
  -enable-kvm \
  -m 2G \
  -smp 2
QEMU 옵션 설명:
  • -kernel: 부팅할 커널 이미지
  • -hda: 루트 파일시스템 이미지
  • -append: 커널 부트 파라미터
  • -nographic: 그래픽 없이 직렬 콘솔만 사용
  • -enable-kvm: KVM 가속 활성화 (훨씬 빠름)
  • -m: 메모리 크기
  • -smp: CPU 코어 수

빠른 테스트 루프 구성

커널 개발 효율은 "수정 후 부팅까지 걸리는 시간"에 크게 좌우됩니다. 아래 루프는 빌드와 부팅 반복 시간을 줄이는 기본 패턴입니다.

  1. 초기 full build: 전체 빌드로 기준 이미지 생성
  2. 이후 incremental build: 변경 범위만 재컴파일
  3. 직렬 콘솔 부팅: GUI 없이 즉시 로그 확인
  4. 실패 시 로그 캡처: 다음 수정에 바로 반영
# 빠른 반복 실행 예시
make -j$(nproc)
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -hda /path/to/rootfs.ext4 \
  -append "root=/dev/sda rw console=ttyS0 panic=-1" \
  -nographic -enable-kvm -m 2G -smp 2 \
  | tee qemu-boot.log

자주 발생하는 부팅 실패와 대응

증상 원인 후보 대응
VFS panic: unable to mount root fs 루트 디바이스 경로/드라이버 불일치 root= 파라미터와 저장장치 드라이버 설정 재확인
부팅 즉시 재시작 초기 패닉 후 자동 재부팅 panic=-1로 정지시켜 로그 확인
콘솔 출력 없음 콘솔 장치 지정 불일치 console=ttyS0 또는 아키텍처별 직렬 장치 재검토
KVM 사용 불가 가상화 미활성 또는 권한 부족 /dev/kvm 권한, BIOS 가상화 옵션 점검

virtme-ng: 빠른 커널 테스트

virtme-ng는 별도의 rootfs 이미지 없이 호스트 파일시스템을 직접 공유하여 빌드한 커널을 즉시 부팅하는 도구입니다. 전통적인 QEMU + rootfs 방식보다 설정이 간단하고 반복 속도가 훨씬 빠릅니다.

virtme-ng 설치

# pip로 설치
pip3 install virtme-ng

# 또는 소스에서 설치
git clone https://github.com/arighi/virtme-ng.git
cd virtme-ng
pip3 install .

# QEMU 필요 (이미 설치되어 있다면 생략)
sudo apt install qemu-system-x86

기본 사용법

# 커널 소스 디렉토리에서
cd /path/to/linux

# 빌드 + 부팅 한 번에 (가장 빠른 방법)
vng --build --run

# 빌드만
vng --build

# 이미 빌드된 커널로 부팅
vng --run

# 커널 설정 + 빌드 + 부팅
vng --build --run --config defconfig

# 추가 커널 파라미터
vng --run --append "debug loglevel=8"

# 특정 명령 실행 후 종료
vng --run --exec "uname -r && cat /proc/version"

# GDB 디버깅 모드
vng --run --gdb
# 다른 터미널에서: gdb vmlinux -ex "target remote :1234"

# 네트워크 활성화
vng --run --net user

# CPU/메모리 설정
vng --run --cpus 4 --memory 4G

virtme-ng vs 전통 QEMU 비교

항목 전통 QEMU + rootfs virtme-ng
rootfs 준비 Buildroot/debootstrap으로 별도 생성 호스트 파일시스템 공유 (준비 불요)
첫 부팅까지 시간 rootfs 생성 30분+ 빌드 5분+ 빌드 후 즉시 (~15초)
호스트 도구 사용 rootfs에 별도 설치 필요 호스트 도구 그대로 사용
커널 모듈 테스트 rootfs에 모듈 복사 필요 자동으로 모듈 경로 공유
격리 수준 완전 격리 호스트 FS 읽기 전용 공유
네트워크 테스트 전체 네트워크 스택 테스트 가능 기본적 네트워크 테스트 가능
적합한 용도 종합 테스트, 배포 검증 빠른 반복 개발, 기능 검증
개발 루프 최적화: 일상적인 수정→빌드→테스트 루프에서는 virtme-ng를 사용하고, 최종 검증 단계에서만 전통적인 QEMU + rootfs 방식을 사용하면 개발 속도가 크게 향상됩니다.

initramfs 직접 생성

initramfs는 커널이 부팅 초기에 사용하는 메모리 기반 루트 파일시스템입니다. BusyBox를 사용하면 최소한의 크기로 기능적인 initramfs를 만들 수 있어, 별도 디스크 이미지 없이 빠른 부팅 테스트가 가능합니다.

BusyBox 기반 initramfs

# 1. BusyBox 다운로드 & 정적 빌드
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1

make defconfig
# 정적 링크 활성화 (중요!)
sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
make -j$(nproc)
make install   # _install/ 에 설치됨

# 2. initramfs 디렉토리 구성
mkdir -p /tmp/initramfs
cd /tmp/initramfs

# BusyBox 바이너리 복사
cp -a /path/to/busybox-1.36.1/_install/* .

# 필수 디렉토리 생성
mkdir -p proc sys dev etc tmp run var/log

# 3. init 스크립트 생성
cat > init <<'INIT_EOF'
#!/bin/sh
echo "=== initramfs booting ==="

# 가상 파일시스템 마운트
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts

# 커널 정보 출력
echo "Kernel: $(uname -r)"
echo "Arch:   $(uname -m)"

# 네트워크 (선택)
ip link set lo up 2>/dev/null

# 셸 실행
echo "=== Dropping to shell ==="
exec /bin/sh
INIT_EOF

chmod +x init

# 4. initramfs cpio 아카이브 생성
find . | cpio -o -H newc | gzip > /tmp/initramfs.cpio.gz

# 5. QEMU로 부팅 테스트
qemu-system-x86_64 \
  -kernel /path/to/linux/arch/x86/boot/bzImage \
  -initrd /tmp/initramfs.cpio.gz \
  -append "console=ttyS0 rdinit=/init" \
  -nographic -enable-kvm -m 1G

커널 내장 initramfs

# 커널 설정에서 initramfs 경로 지정
./scripts/config --enable BLK_DEV_INITRD
./scripts/config --set-str INITRAMFS_SOURCE "/tmp/initramfs"

# 빌드하면 initramfs가 커널에 내장됨
make -j$(nproc)

# 별도 -initrd 없이 부팅 가능
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -append "console=ttyS0" \
  -nographic -enable-kvm -m 1G

고급 initramfs 구성

# 커널 모듈을 initramfs에 포함
cd /path/to/linux
make modules_install INSTALL_MOD_PATH=/tmp/initramfs

# 테스트용 커스텀 프로그램 추가
cat > /tmp/test_module.c <<'EOF'
#include <linux/module.h>
#include <linux/kernel.h>
static int __init hello_init(void) {
    pr_info("Hello from initramfs!\n");
    return 0;
}
static void __exit hello_exit(void) {
    pr_info("Goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
EOF

# init 스크립트에 모듈 로드 추가
# insmod /lib/modules/$(uname -r)/hello.ko
initramfs 부팅 흐름 initramfs 부팅 흐름 BIOS/UEFI 부트로더 실행 bzImage 로드 initrd 로드 커널 초기화 메모리 설정 디바이스 초기화 initramfs 해제 initramfs 마운트 tmpfs → / cpio 아카이브 해제 메모리에 FS 구성 /init 실행 proc/sys 마운트 모듈 로드 디바이스 탐색 테스트 모드 셸 직접 실행 exec /bin/sh 디버깅/테스트 정상 부팅 실제 rootfs 마운트 switch_root systemd/init initramfs 디렉토리 구조 / ├── init (PID 1 - 부팅 스크립트) ├── bin/ (BusyBox 심볼릭 링크: sh, ls, mount, ...) ├── sbin/ (ifconfig, insmod, modprobe, ...) ├── usr/ (추가 유틸리티) ├── proc/ sys/ dev/ (가상 FS 마운트 포인트) ├── etc/ (설정 파일) └── lib/modules/ (커널 모듈 - 선택) 크기 비교 BusyBox 정적 바이너리: ~2MB 최소 initramfs (gzip): ~1MB 모듈 포함 initramfs: ~50MB Buildroot rootfs.ext4: ~200MB+ Debian debootstrap: ~500MB+
그림: initramfs 부팅 흐름 - BIOS에서 셸까지
initramfs vs rootfs 선택 기준:
  • initramfs: 빠른 부팅 테스트, 모듈 로드 테스트, 드라이버 초기화 검증
  • Buildroot rootfs: 네트워크 테스트, 사용자 공간 연동, 장시간 스트레스 테스트
  • Debian rootfs: 완전한 사용자 환경, 패키지 설치, 종합 시스템 테스트
  • virtme-ng: 호스트 FS 공유, 가장 빠른 반복 주기, 일상 개발

크로스 컴파일 환경

x86_64 호스트에서 ARM, ARM64, RISC-V 등 다른 아키텍처용 커널을 빌드하려면 크로스 컴파일 툴체인이 필요합니다.

크로스 컴파일러 설치

# Ubuntu/Debian
sudo apt install -y \
  gcc-arm-linux-gnueabi \
  gcc-arm-linux-gnueabihf \
  gcc-aarch64-linux-gnu \
  gcc-riscv64-linux-gnu

# Fedora/RHEL
sudo dnf install -y \
  gcc-arm-linux-gnu \
  gcc-aarch64-linux-gnu \
  gcc-riscv64-linux-gnu

# Arch Linux
sudo pacman -S \
  arm-none-eabi-gcc \
  aarch64-linux-gnu-gcc

크로스 컴파일 빌드

# ARM (32-bit)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- multi_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)

# ARM64 (64-bit)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# RISC-V (64-bit)
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig
make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j$(nproc)

크로스 컴파일 커널 QEMU 부팅

# ARM64 커널 QEMU 부팅
qemu-system-aarch64 \
  -M virt \
  -cpu cortex-a57 \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0" \
  -nographic \
  -m 2G

# RISC-V 커널 QEMU 부팅
qemu-system-riscv64 \
  -M virt \
  -kernel arch/riscv/boot/Image \
  -append "console=ttyS0" \
  -nographic \
  -m 2G

크로스 컴파일 검증 체크리스트

크로스 컴파일된 커널이 제대로 빌드되고 동작하는지 체계적으로 검증하는 것이 중요합니다. 아래 체크리스트는 빌드부터 부팅 검증까지 전체 과정을 안내합니다.

크로스 컴파일 검증 체크리스트 1단계: 빌드 전 검증 (arm64 예시) ☐ 툴체인 버전 확인 $ aarch64-linux-gnu-gcc --version (≥ 9.0 권장) ☐ ARCH / CROSS_COMPILE 환경 변수 설정 $ export ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- ☐ make defconfig 실행 2단계: 빌드 검증 (arm64 예시) ☐ 크로스 컴파일 빌드 실행 $ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) ☐ 커널 이미지 파일 확인 $ file arch/arm64/boot/Image → ARM aarch64 확인 ☐ 빌드 경고 / 오류 없음 확인 3단계: 바이너리 분석 검증 ☐ ELF 헤더 확인 (readelf) $ readelf -h vmlinux → Machine: AArch64, Class: ELF64 ☐ 심볼 테이블 확인 (nm) ☐ 디스어셈블 샘플 확인 $ aarch64-linux-gnu-objdump -d vmlinux | head → ARM64 명령어 (adrp, ldr, str 등) ☐ 커널 바이너리 크기 확인 ☐ 커널 버전 문자열 검사 $ strings vmlinux | grep "Linux version" → ARCH=arm64 포함 여부 확인 ☐ 모듈 빌드 확인 (선택) 4단계: QEMU 부팅 테스트 ☐ qemu-system-aarch64 으로 부팅 실행 $ qemu-system-aarch64 -M virt -cpu cortex-a57 -kernel arch/arm64/boot/Image ☐ 콘솔에서 부팅 메시지 확인 → "Linux version ... SMP ... ARM64 ..." ☐ 커널 패닉 없이 부팅 완료 ☐ init 프로세스 실행 확인 ☐ 기본 명령어 동작 확인 (ls, cat, uname) $ uname -m → aarch64 5단계: 런타임 동작 검증 ☐ /proc/cpuinfo 아키텍처 확인 $ cat /proc/cpuinfo | grep "CPU architecture" → CPU architecture : 8 (AArch64) ☐ /proc/version 커널 정보 확인 ☐ 커널 모듈 로드 테스트 $ insmod hello.ko && rmmod hello → dmesg 로 로드 메시지 확인 ☐ 네트워크 / 블록 디바이스 동작 확인 ☐ 스트레스 테스트 (선택) $ stress-ng --cpu 4 --timeout 60s → 시스템 안정성 검증 ☐ 커널 로그 이상 없음 확인 (dmesg)
그림: 크로스 컴파일 검증 체크리스트 - 빌드부터 런타임까지 5단계 검증
아키텍처 ARCH 변수 툴체인 접두사 QEMU 시스템 커널 이미지 경로
ARM (32-bit) arm arm-linux-gnueabihf- qemu-system-arm arch/arm/boot/zImage
ARM64 (64-bit) arm64 aarch64-linux-gnu- qemu-system-aarch64 arch/arm64/boot/Image
RISC-V (64-bit) riscv riscv64-linux-gnu- qemu-system-riscv64 arch/riscv/boot/Image
PowerPC (64-bit) powerpc powerpc64-linux-gnu- qemu-system-ppc64 arch/powerpc/boot/vmlinux
MIPS (32-bit) mips mips-linux-gnu- qemu-system-mips arch/mips/boot/vmlinux
💡

크로스 컴파일 문제 해결:

  • 빌드 실패: ARCHCROSS_COMPILE이 모든 make 명령에 포함되었는지 확인
  • QEMU 부팅 실패: -M 옵션으로 올바른 머신 타입 지정 (예: -M virt)
  • 심볼 누락: CONFIG_DEBUG_INFO 활성화 후 재빌드
  • 모듈 로드 실패: 커널 버전과 모듈 빌드 환경이 일치하는지 확인
  • 환경 변수 설정: Makefile에 export ARCH=arm64, export CROSS_COMPILE=... 추가

크로스 컴파일 사전 점검 루틴

크로스 빌드 실패의 다수는 소스 문제가 아니라 툴체인/환경 변수 불일치에서 발생합니다. 아래 순서를 먼저 실행하면 실패 원인을 빠르게 분리할 수 있습니다.

# 1) 툴체인 확인
which aarch64-linux-gnu-gcc
aarch64-linux-gnu-gcc -dumpmachine

# 2) 환경 변수 확인
echo "ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE"

# 3) 산출물 아키텍처 확인
file arch/arm64/boot/Image
readelf -h vmlinux | grep "Machine"
주의: 쉘 세션마다 ARCH, CROSS_COMPILE 값이 달라지면 재현이 어렵습니다. 프로젝트별 쉘 스크립트(env-arm64.sh 등)로 환경을 고정하는 방식을 권장합니다.

GDB/KGDB 디버거 설정

GDB를 사용하면 QEMU 가상 머신에서 실행 중인 커널을 소스 레벨에서 디버깅할 수 있습니다. 브레이크포인트 설정, 스택 추적, 변수 검사 등이 가능합니다.

커널 GDB 디버깅 환경 구성

# 커널 설정에서 디버깅 옵션 활성화
cd /path/to/linux
./scripts/config --enable DEBUG_INFO
./scripts/config --enable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
./scripts/config --enable GDB_SCRIPTS
./scripts/config --enable FRAME_POINTER
./scripts/config --disable DEBUG_INFO_REDUCED
make olddefconfig
make -j$(nproc)

# QEMU를 GDB 서버 모드로 실행 (1234 포트 대기)
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -hda /path/to/rootfs.ext4 \
  -append "root=/dev/sda rw console=ttyS0 nokaslr" \
  -nographic \
  -s -S \
  -m 2G \
  -smp 2

# 다른 터미널에서 GDB 실행
gdb vmlinux
QEMU 디버깅 옵션:
  • -s: TCP 1234 포트에서 GDB 서버 시작 (-gdb tcp::1234와 동일)
  • -S: 시작 시 CPU를 일시 정지 (GDB 연결 대기)
  • nokaslr: KASLR 비활성화 (주소 고정으로 디버깅 용이)

GDB 명령어 예제

# GDB 프롬프트에서

# QEMU에 연결
(gdb) target remote :1234

# 커널 GDB 스크립트 로드 (선택, 자동 로드되지 않는 경우)
(gdb) source vmlinux-gdb.py

# 브레이크포인트 설정
(gdb) break start_kernel
(gdb) break do_sys_open

# 실행 계속
(gdb) continue

# 스택 추적
(gdb) backtrace

# 변수 출력
(gdb) print init_task

# 구조체 필드 출력
(gdb) print init_task.comm

# 메모리 덤프
(gdb) x/10x 0xffffffff81000000

# 커널 전용 명령 (vmlinux-gdb.py 제공)
(gdb) lx-dmesg         # 커널 로그 출력
(gdb) lx-lsmod         # 로드된 모듈 목록
(gdb) lx-ps            # 프로세스 목록

QEMU-GDB 통합 워크플로

QEMU와 GDB를 연동한 커널 디버깅은 여러 단계를 거칩니다. 아래 다이어그램은 빌드부터 디버깅까지 전체 흐름을 보여줍니다.

QEMU-GDB 통합 디버깅 워크플로 1단계: 디버그 심볼 포함 커널 빌드 .config 설정 CONFIG_DEBUG_INFO=y CONFIG_GDB_SCRIPTS=y CONFIG_FRAME_POINTER=y 빌드 실행 $ make -j$(nproc) → vmlinux (심볼 포함) → bzImage (부팅 이미지) 2단계: 터미널 분할 실행 터미널 1: QEMU (GDB 서버 모드) $ qemu-system-x86_64 \ -kernel arch/x86/boot/bzImage \ -append "nokaslr console=ttyS0" \ -s -S -nographic -m 2G -s: TCP:1234 GDB 서버 | -S: 시작 중지 터미널 2: GDB (클라이언트) $ gdb vmlinux (gdb) target remote :1234 (gdb) break start_kernel (gdb) continue vmlinux: 디버그 심볼 포함 ELF TCP:1234 3단계: 디버깅 작업 (GDB 명령어) ① 브레이크포인트 break function_name break file.c:123 break *0xffffffff81000000 info breakpoints ② 실행 제어 continue (c) step (s) - 단계 실행 next (n) - 다음 줄 finish - 함수 종료까지 ③ 변수/메모리 검사 print variable print *ptr x/10x $rsp - 메모리 덤프 info registers ④ 커널 전용 명령 lx-dmesg lx-ps lx-lsmod lx-symbols ⑤ 스택/코드 탐색 backtrace (bt) frame N - 프레임 전환 list - 소스 코드 보기 ⑥ 조건부/워치포인트 watch variable condition N expr catch syscall open ⑦ 멀티코어/쓰레드 info threads thread N - CPU 전환 set scheduler-locking on ⑧ 스크립팅 source script.gdb define my_cmd python ... 디버깅 팁 KASLR 비활성화: 부팅 옵션에 nokaslr 추가 (주소 고정) 심볼 재로드: 모듈 로드 후 lx-symbols 명령으로 심볼 업데이트 TUI 모드: GDB에서 Ctrl+X A로 소스 코드 창 활성화
그림: QEMU-GDB 통합 디버깅 워크플로 - 빌드부터 디버깅까지 전체 과정
💡

QEMU-GDB 디버깅 체크리스트:

  1. 커널 설정에서 CONFIG_DEBUG_INFO, CONFIG_GDB_SCRIPTS 활성화
  2. 부팅 옵션에 nokaslr 추가 (주소 고정)
  3. QEMU를 -s -S 옵션으로 시작 (GDB 대기)
  4. GDB에서 vmlinux 로드 (bzImage가 아님!)
  5. target remote :1234로 QEMU 연결
  6. 브레이크포인트 설정 후 continue

주의: vmlinux는 디버그 심볼이 포함된 ELF 파일이고, bzImage는 압축된 부팅 이미지입니다. GDB에는 vmlinux를 사용해야 합니다.

KGDB (실제 하드웨어 디버깅)

KGDB는 실제 하드웨어에서 직렬 포트나 네트워크를 통해 커널을 디버깅할 수 있게 해줍니다.

# 커널 설정
./scripts/config --enable KGDB
./scripts/config --enable KGDB_SERIAL_CONSOLE
./scripts/config --enable KGDB_KDB
make olddefconfig
make -j$(nproc)

# 부트 파라미터에 KGDB 옵션 추가
# kgdboc=ttyS0,115200 kgdbwait
주의: KGDB는 시스템 전체를 멈추므로, 프로덕션 환경에서는 절대 사용하지 마세요. 개발/디버깅 전용입니다.

첫 디버깅 세션 권장 시나리오

처음에는 복잡한 경로 대신 부팅 초기 경로 하나만 추적하는 방식이 가장 학습 효율이 좋습니다.

  1. start_kernel 브레이크포인트 설정
  2. continue 후 진입 확인
  3. bt로 초기 콜스택 확인
  4. next/step으로 서브시스템 초기화 흐름 파악
  5. 관찰 결과를 텍스트로 기록해 다음 세션 기준점으로 사용
(gdb) target remote :1234
(gdb) break start_kernel
(gdb) continue
(gdb) bt
(gdb) next
(gdb) list

디버그 커널 설정 옵션 총정리

커널에는 수백 개의 디버깅 관련 설정 옵션이 있습니다. 목적에 맞는 옵션을 선택적으로 활성화하면 디버깅 효율이 크게 향상됩니다. 주요 옵션을 카테고리별로 정리합니다.

디버그 정보 옵션

설정 옵션 기능 오버헤드 용도
CONFIG_DEBUG_INFO DWARF 디버그 정보 포함 vmlinux 크기 5~10배 증가 GDB 디버깅 필수
CONFIG_DEBUG_INFO_DWARF5 DWARF v5 형식 사용 DWARF4 대비 크기 절감 최신 GDB/LLVM 권장
CONFIG_GDB_SCRIPTS 커널 GDB Python 스크립트 없음 lx-dmesg, lx-ps 등 사용
CONFIG_FRAME_POINTER 프레임 포인터 유지 성능 1~2% 감소 정확한 스택 추적
CONFIG_DEBUG_INFO_BTF BPF Type Format 생성 빌드 시간 약간 증가 BPF/bpftrace 필수
CONFIG_KALLSYMS_ALL 모든 심볼을 /proc/kallsyms에 커널 크기 약간 증가 perf, ftrace 심볼 해석

메모리 디버깅 옵션

설정 옵션 기능 오버헤드 검출 대상
CONFIG_KASAN Kernel Address Sanitizer 메모리 3~5배, 성능 50% 감소 use-after-free, buffer overflow
CONFIG_KASAN_GENERIC KASAN 일반 모드 가장 정확, 가장 느림 개발 중 전면 검사
CONFIG_KASAN_SW_TAGS KASAN 소프트웨어 태그 모드 중간 오버헤드 (ARM64) ARM64 전용 경량 검사
CONFIG_KMSAN Kernel Memory Sanitizer 큰 오버헤드 초기화되지 않은 메모리 사용
CONFIG_KFENCE Kernel Electric Fence 거의 없음 (샘플링) 프로덕션 환경 메모리 버그
CONFIG_SLUB_DEBUG SLUB 할당자 디버깅 약간의 성능 감소 slab corruption, 이중 해제
CONFIG_DEBUG_PAGEALLOC 페이지 해제 시 언매핑 큰 성능 감소 use-after-free (페이지 레벨)
CONFIG_DEBUG_SLAB SLAB 포이즌/레드존 성능 감소 slab 오버런, corruption

동기화/락 디버깅 옵션

설정 옵션 기능 검출 대상
CONFIG_LOCKDEP 락 의존성 추적 데드락, 순환 의존성, 잘못된 락 순서
CONFIG_PROVE_LOCKING 락 정확성 증명 런타임에 락 규칙 위반 감지
CONFIG_LOCK_STAT 락 통계 수집 /proc/lock_stat으로 경합 분석
CONFIG_DEBUG_MUTEXES 뮤텍스 디버깅 잘못된 뮤텍스 사용 (재귀 등)
CONFIG_DEBUG_SPINLOCK 스핀락 디버깅 초기화되지 않은 스핀락 사용
CONFIG_DEBUG_RWSEMS RW 세마포어 디버깅 잘못된 rw_semaphore 사용
CONFIG_DEBUG_ATOMIC_SLEEP 원자 컨텍스트 sleep 감지 인터럽트/스핀락 중 sleep 호출
CONFIG_DETECT_HUNG_TASK 행 태스크 감지 120초 이상 TASK_UNINTERRUPTIBLE
CONFIG_WW_MUTEX_SELFTEST Wait/Wound 뮤텍스 셀프테스트 부팅 시 ww_mutex 정확성 검증

기타 디버깅 옵션

설정 옵션 기능 용도
CONFIG_DYNAMIC_DEBUG pr_debug() 동적 활성화 런타임에 선택적 디버그 로그 on/off
CONFIG_DEBUG_FS debugfs 파일시스템 커널 내부 상태 노출 (/sys/kernel/debug)
CONFIG_MAGIC_SYSRQ SysRq 키 시스템 행 시 긴급 동작 (sync, 리부트 등)
CONFIG_PANIC_ON_OOPS Oops 시 패닉 Oops 발생 즉시 정지 (디버깅용)
CONFIG_SOFTLOCKUP_DETECTOR 소프트 락업 감지 CPU가 오래 선점 불가 상태
CONFIG_HARDLOCKUP_DETECTOR 하드 락업 감지 CPU가 인터럽트도 처리 못함
CONFIG_UBSAN Undefined Behavior Sanitizer 정수 오버플로, 배열 인덱스 초과
CONFIG_KCSAN Kernel Concurrency Sanitizer 데이터 레이스 감지
CONFIG_FTRACE 함수 추적 인프라 함수 호출 추적, 이벤트 트레이싱
CONFIG_FUNCTION_TRACER 함수 호출 추적기 모든 함수 진입/종료 기록

목적별 디버그 프로필

# 프로필 1: 기본 디버깅 (GDB + 로그)
./scripts/config --enable DEBUG_INFO
./scripts/config --enable DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT
./scripts/config --enable GDB_SCRIPTS
./scripts/config --enable FRAME_POINTER
./scripts/config --enable DYNAMIC_DEBUG
./scripts/config --enable DEBUG_FS
./scripts/config --enable MAGIC_SYSRQ

# 프로필 2: 메모리 버그 헌팅
./scripts/config --enable KASAN
./scripts/config --enable KASAN_GENERIC
./scripts/config --enable KFENCE
./scripts/config --enable SLUB_DEBUG
./scripts/config --enable DEBUG_PAGEALLOC
./scripts/config --enable PAGE_OWNER

# 프로필 3: 동기화 버그 헌팅
./scripts/config --enable LOCKDEP
./scripts/config --enable PROVE_LOCKING
./scripts/config --enable DEBUG_MUTEXES
./scripts/config --enable DEBUG_SPINLOCK
./scripts/config --enable DEBUG_ATOMIC_SLEEP
./scripts/config --enable DETECT_HUNG_TASK
./scripts/config --enable KCSAN

# 프로필 4: 성능 분석
./scripts/config --enable FTRACE
./scripts/config --enable FUNCTION_TRACER
./scripts/config --enable FUNCTION_GRAPH_TRACER
./scripts/config --enable KALLSYMS_ALL
./scripts/config --enable DEBUG_INFO_BTF

# 적용
make olddefconfig
디버그 옵션 카테고리별 관계도 CONFIG_DEBUG_KERNEL 디버그 정보 DEBUG_INFO GDB_SCRIPTS FRAME_POINTER KALLSYMS_ALL GDB, perf, ftrace 메모리 검증 KASAN (use-after-free) KMSAN (uninit memory) KFENCE (low-overhead) SLUB_DEBUG / PAGEALLOC 메모리 corruption 검출 동기화 검증 LOCKDEP (deadlock) PROVE_LOCKING KCSAN (data race) DEBUG_ATOMIC_SLEEP 데드락, 레이스 검출 런타임 모니터링 FTRACE / FUNCTION_TRACER DYNAMIC_DEBUG UBSAN (undefined behav.) SOFTLOCKUP / HARDLOCKUP 추적, 행 감지, UB 검출 Sanitizer 조합 가이드 함께 사용 가능 KASAN + LOCKDEP + UBSAN + DEBUG_ATOMIC_SLEEP → 개발 중 최대 검출 조합 (성능 크게 저하됨) 동시 사용 불가 KASAN + KMSAN (상호 배타) → 별도 빌드 프로필로 분리하여 각각 테스트 프로덕션 권장 최소 세트 KFENCE + SOFTLOCKUP_DETECTOR + HARDLOCKUP_DETECTOR + DETECT_HUNG_TASK → 거의 무시할 수 있는 오버헤드로 런타임 버그 샘플링 검출
그림: 디버그 옵션 카테고리별 관계도 - 목적에 맞는 옵션 선택 가이드

ftrace/perf 기본 설정

ftrace와 perf는 커널 성능 분석과 동작 추적의 핵심 도구입니다. 커널 설정에서 관련 옵션을 활성화하고 기본 사용법을 익혀두면 디버깅 효율이 크게 향상됩니다.

ftrace 기본 사용

# 커널 설정 (필수)
./scripts/config --enable FTRACE
./scripts/config --enable FUNCTION_TRACER
./scripts/config --enable FUNCTION_GRAPH_TRACER
./scripts/config --enable DYNAMIC_FTRACE
./scripts/config --enable STACK_TRACER

# ftrace 기본 사용 (debugfs 마운트 필요)
mount -t debugfs nodev /sys/kernel/debug

# 사용 가능한 트레이서 확인
cat /sys/kernel/debug/tracing/available_tracers

# 함수 트레이서 활성화
echo function > /sys/kernel/debug/tracing/current_tracer

# 특정 함수만 추적
echo "do_sys_openat2" > /sys/kernel/debug/tracing/set_ftrace_filter

# 추적 시작/중지
echo 1 > /sys/kernel/debug/tracing/tracing_on
# ... 작업 수행 ...
echo 0 > /sys/kernel/debug/tracing/tracing_on

# 결과 확인
cat /sys/kernel/debug/tracing/trace

# function_graph 트레이서 (호출 깊이 표시)
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo "tcp_sendmsg" > /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on

trace-cmd 사용

# trace-cmd 설치
sudo apt install trace-cmd

# 함수 추적 녹화
trace-cmd record -p function -l "do_sys_*"
# Ctrl+C로 중지

# 결과 보기
trace-cmd report | head -50

# function_graph 녹화
trace-cmd record -p function_graph -g tcp_sendmsg

# 이벤트 추적 (스케줄러)
trace-cmd record -e sched_switch -e sched_wakeup
trace-cmd report

perf 기본 사용

# perf 설치
sudo apt install linux-tools-common linux-tools-$(uname -r)

# 또는 커널 소스에서 빌드
make -C tools/perf install

# CPU 프로파일링 (10초간 시스템 전체)
perf record -a -g -- sleep 10
perf report

# 특정 프로세스 프로파일링
perf record -g -p $PID -- sleep 10

# 하드웨어 이벤트 카운터
perf stat -e cycles,instructions,cache-misses,branch-misses -- make -j$(nproc)

# 콜 그래프 시각화 (Flamegraph)
perf record -a -g -- sleep 30
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

# 커널 함수 프로파일링
perf top -e cycles:k # 커널 모드만
추적/프로파일링 선택 가이드:
  • ftrace: 함수 호출 순서, 실행 시간, 특정 경로 추적 → "이 함수가 언제 어떤 순서로 호출되나?"
  • perf: 통계적 프로파일링, 핫스팟 분석, 하드웨어 카운터 → "어디서 CPU 시간을 가장 많이 쓰나?"
  • bpftrace: 유연한 동적 추적, 커스텀 집계 → "특정 조건에서 특정 값 분포는?"
  • trace-cmd: ftrace의 사용자 친화적 프론트엔드 → "ftrace를 쉽게 쓰고 싶다"

빌드 속도 최적화

ccache (컴파일러 캐시)

ccache는 컴파일 결과를 캐싱하여 재빌드 속도를 크게 향상시킵니다.

# ccache 설치 (이미 위에서 설치됨)
sudo apt install ccache

# ccache 캐시 크기 설정 (기본 5GB, 10GB 권장)
ccache -M 10G

# ccache 통계 확인
ccache -s

# 커널 빌드 시 ccache 사용
make CC="ccache gcc" -j$(nproc)

# 또는 PATH에 ccache 심볼릭 링크 추가
export PATH="/usr/lib/ccache:$PATH"
make -j$(nproc)

distcc (분산 컴파일)

여러 머신을 사용하여 병렬로 컴파일하면 빌드 시간을 대폭 단축할 수 있습니다.

# 서버 머신에서 distccd 실행
sudo apt install distcc
distccd --daemon --allow 192.168.1.0/24

# 클라이언트 머신에서 빌드
export DISTCC_HOSTS="localhost 192.168.1.100 192.168.1.101"
make CC="distcc gcc" -j16
주의: 분산 빌드는 네트워크 지연과 전처리 비용에 따라 오히려 느려질 수 있습니다. 먼저 ccache만으로 이득을 확인한 뒤, 대형 소스 트리에서만 distcc를 추가하는 순서가 안전합니다.

빌드 환경 성능 튜닝

커널 빌드 성능은 디스크 I/O, 병렬 작업 수, 캐시 효율에 크게 좌우됩니다. 아래 기법으로 빌드 시간을 50% 이상 단축할 수 있습니다.

tmpfs 빌드 (RAM 디스크)

# 별도 빌드 디렉토리를 tmpfs에 마운트
sudo mkdir -p /mnt/kbuild
sudo mount -t tmpfs -o size=15G tmpfs /mnt/kbuild

# 소스와 빌드 디렉토리 분리 (O= 옵션)
cd /home/user/linux
make O=/mnt/kbuild defconfig
make O=/mnt/kbuild -j$(nproc)

# 영구 설정: /etc/fstab에 추가
# tmpfs /mnt/kbuild tmpfs size=15G,mode=1777 0 0
주의: tmpfs는 RAM을 사용합니다. 최소 32GB RAM에서 15GB 할당을 권장합니다. 시스템 메모리가 부족하면 OOM Killer가 작동할 수 있습니다.

병렬 작업 수 최적화

# 기본: CPU 코어 수
make -j$(nproc)

# 코어 수 + 2 (I/O 대기 보상)
make -j$(( $(nproc) + 2 ))

# 메모리 제한 고려: 코어당 2GB 필요 (LTO 시)
# 16GB RAM, 8코어 → -j8이 안전
# 8GB RAM, 8코어 → -j4 권장

# 백그라운드 빌드 (낮은 우선순위)
nice -n 19 ionice -c3 make -j$(nproc)

증분 빌드 최적화

기법 명령 효과
단일 파일 빌드 make drivers/net/my_driver.o 컴파일 오류만 빠르게 확인
단일 디렉토리 빌드 make drivers/net/ 서브시스템 전체 빌드
모듈만 빌드 make modules vmlinux 재링크 건너뜀
ccache + 분리 빌드 make CC="ccache gcc" O=build/ 캐시 히트로 재빌드 10초 이내
전처리만 확인 make drivers/net/my_driver.i 매크로 전개 결과 확인
어셈블리 확인 make drivers/net/my_driver.s 컴파일러 출력 코드 확인
커널 빌드 성능 최적화 비교 빌드 시간 비교 (x86_64 defconfig, 8코어 기준) 기본 HDD -j1 ~45분 SSD -j8 ~8분 SSD + ccache -j8 (재빌드) ~1분 tmpfs+ccache -j8 (재빌드) ~30초 증분 빌드 단일 파일 수정 ~10초 virtme-ng 빌드+부팅 ~15초 ※ 실제 시간은 하드웨어, 커널 설정, 변경 범위에 따라 크게 달라질 수 있음 핵심: ccache + SSD + 증분 빌드 조합이 가장 실용적
그림: 커널 빌드 성능 최적화 비교 - 환경별 빌드 시간 차이

정적 분석 도구

Sparse

Sparse는 Linux 커널 전용 정적 분석 도구로, 타입 체크, 엔디안 검증, 락 검증 등을 수행합니다.

# Sparse로 검사하며 빌드
make C=1 -j$(nproc)     # 변경된 파일만
make C=2 -j$(nproc)     # 모든 파일

Coccinelle

Coccinelle은 의미론적 패치 언어(SmPL)를 사용하여 C 코드를 자동 변환하고 버그를 찾습니다.

# Ubuntu/Debian
sudo apt install coccinelle

# 커널 소스에서 Coccinelle 검사 실행
make coccicheck MODE=report

checkpatch.pl 상세 사용법

checkpatch.pl은 커널 코딩 스타일(Documentation/process/coding-style.rst)을 자동 검사하는 필수 도구입니다. 업스트림 패치 제출 전에 반드시 통과해야 합니다.

# 파일 직접 검사
./scripts/checkpatch.pl --file drivers/net/my_driver.c

# 패치 파일 검사
./scripts/checkpatch.pl 0001-my-patch.patch

# 마지막 커밋 검사
git diff HEAD~1 | ./scripts/checkpatch.pl -

# 마지막 N개 커밋 각각 검사
git format-patch -3 --stdout | ./scripts/checkpatch.pl

# 엄격 모드 (추가 경고 포함)
./scripts/checkpatch.pl --strict --file drivers/net/my_driver.c

# 특정 검사 항목 무시
./scripts/checkpatch.pl --ignore LONG_LINE,TRAILING_WHITESPACE --file my_file.c

# 사용 가능한 검사 항목 목록
./scripts/checkpatch.pl --list-types
검사 유형 심각도 설명 예시
ERROR 필수 수정 코딩 스타일 위반 탭 대신 스페이스, 잘못된 중괄호 위치
WARNING 강력 권고 잠재적 문제 80자 초과, 불필요한 초기화
CHECK 참고 스타일 제안 주석 형식, 정렬 권장
자주 발생하는 checkpatch 지적과 해결:
  • TRAILING_WHITESPACE: 줄 끝 공백 → 에디터에서 자동 제거 설정
  • SPACING: if(if (, for(for (
  • OPEN_BRACE: 함수 정의 중괄호는 다음 줄, 제어문은 같은 줄
  • LONG_LINE: 80자 권장, 100자 이내 유지 (문자열 리터럴 예외)
  • SPLIT_STRING: 로그 메시지 문자열은 가독성을 위해 분할하지 않음
  • UNNECESSARY_ELSE: if 블록이 return으로 끝나면 else 불필요

커널 셀프테스트 (kselftest)

커널 셀프테스트는 커널 기능의 회귀를 자동으로 검출하는 테스트 프레임워크입니다. tools/testing/selftests/에 서브시스템별 테스트가 있으며, 패치 제출 전 관련 테스트를 실행하는 것이 좋습니다.

셀프테스트 실행

# 전체 셀프테스트 빌드 & 실행
make -C tools/testing/selftests run_tests

# 특정 서브시스템 테스트만 실행
make -C tools/testing/selftests TARGETS="net mm" run_tests

# 개별 테스트 빌드
make -C tools/testing/selftests/net

# 크로스 컴파일 셀프테스트
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
  -C tools/testing/selftests TARGETS="bpf"

# 설치 (QEMU rootfs에 복사용)
make -C tools/testing/selftests TARGETS="net" \
  INSTALL_PATH=/path/to/rootfs/kselftest install

주요 테스트 타겟

타겟 테스트 영역 선행 조건
bpf BPF/eBPF 프로그램 CONFIG_BPF_SYSCALL, clang/llvm
net 네트워킹 스택 CONFIG_NET
mm 메모리 관리 CONFIG_USERFAULTFD
cgroup 컨트롤 그룹 CONFIG_CGROUPS
futex Futex 동기화 CONFIG_FUTEX
seccomp Seccomp 필터 CONFIG_SECCOMP
kvm KVM 가상화 CONFIG_KVM
filesystems 파일시스템 공통 다양한 FS CONFIG

테스트 작성 기본 패턴

// tools/testing/selftests/my_subsystem/my_test.c
#include "../kselftest_harness.h"

/* 기본 테스트 */
TEST(my_basic_test)
{
    int result = do_something();

    /* 성공 조건 확인 */
    ASSERT_EQ(result, 0);
    ASSERT_NE(result, -1);
    ASSERT_GT(result, -1);
    EXPECT_TRUE(result >= 0);
}

/* 파라미터화된 테스트 */
FIXTURE(my_fixture)
{
    int fd;
};

FIXTURE_SETUP(my_fixture)
{
    self->fd = open("/dev/mydev", O_RDWR);
    ASSERT_GE(self->fd, 0);
}

FIXTURE_TEARDOWN(my_fixture)
{
    close(self->fd);
}

TEST_F(my_fixture, read_test)
{
    char buf[64];
    ssize_t n = read(self->fd, buf, sizeof(buf));
    ASSERT_GT(n, 0);
}

TEST_HARNESS_MAIN
# tools/testing/selftests/my_subsystem/Makefile
TEST_GEN_PROGS := my_test
include ../lib.mk
kselftest 활용 팁:
  • KSFT_SKIP 반환으로 선행 조건 미충족 시 테스트 건너뛰기
  • make -C tools/testing/selftests TARGETS="net" summary=1로 결과 요약
  • QEMU에서 실행 시 INSTALL_PATH로 rootfs에 테스트 복사 후 실행
  • CI 파이프라인에 셀프테스트 포함하여 자동 회귀 검사

일반적인 개발 워크플로

  1. 소스 다운로드
    git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    cd linux
  2. 브랜치 생성
    git checkout -b my-feature
  3. 설정 및 빌드
    make defconfig
    ./scripts/config --enable DEBUG_INFO
    make -j$(nproc)
  4. 코드 탐색 인덱스 생성
    make tags cscope
    ./scripts/clang-tools/gen_compile_commands.py
  5. 코드 수정 및 테스트
    # 에디터로 코드 수정
    vim drivers/my_driver.c
    
    # 재빌드
    make -j$(nproc)
    
    # QEMU 테스트
    qemu-system-x86_64 -kernel arch/x86/boot/bzImage ...
  6. 코딩 스타일 검사
    ./scripts/checkpatch.pl --file drivers/my_driver.c
  7. 정적 분석
    make C=2 drivers/my_driver.o
  8. 패치 생성
    git add drivers/my_driver.c
    git commit -s
    git format-patch -1

Git 커널 패치 워크플로 심화

리눅스 커널은 GitHub PR이 아닌 이메일 기반 패치 워크플로를 사용합니다. git format-patch, git send-email, b4 도구를 익히면 업스트림 기여가 가능해집니다.

패치 생성 (git format-patch)

# 마지막 커밋 1개를 패치로 생성
git format-patch -1

# 마지막 3개 커밋을 패치 시리즈로 생성
git format-patch -3 --cover-letter

# 특정 브랜치와의 차이를 패치로 생성
git format-patch origin/master..HEAD

# 버전 표시 (v2, v3 등 재전송 시)
git format-patch -1 -v 2

# 패치 출력 디렉토리 지정
git format-patch -3 -o patches/

패치 전송 (git send-email)

# git send-email 설치
sudo apt install git-email

# Git 이메일 설정
git config --global sendemail.smtpserver smtp.gmail.com
git config --global sendemail.smtpserverport 587
git config --global sendemail.smtpencryption tls
git config --global sendemail.smtpuser your-email@gmail.com
git config --global sendemail.from "Your Name <your-email@gmail.com>"

# 패치를 메인테이너에게 전송
# get_maintainer.pl로 수신자 자동 결정
git send-email \
  --to=$(./scripts/get_maintainer.pl --nogit --norolestats 0001-*.patch | head -1) \
  --cc=linux-kernel@vger.kernel.org \
  0001-my-patch.patch

# 패치 시리즈 전송 (커버 레터 포함)
git send-email \
  --to=maintainer@kernel.org \
  --cc=linux-kernel@vger.kernel.org \
  patches/*.patch

메인테이너 & 리뷰어 찾기

# 파일별 메인테이너 찾기
./scripts/get_maintainer.pl -f drivers/net/ethernet/intel/e1000e/netdev.c

# 패치에 대한 메인테이너 찾기
./scripts/get_maintainer.pl 0001-my-patch.patch

# 특정 서브시스템의 메인테이너 트리 확인
grep -A5 "NETWORKING" MAINTAINERS

b4 패치 관리 도구

b4는 커널 메일링 리스트에서 패치 시리즈를 가져오고 적용하는 현대적 도구입니다.

# b4 설치
pip3 install b4

# 메일링 리스트에서 패치 시리즈 다운로드 (Message-ID로)
b4 am <message-id>

# 다운로드한 패치를 적용
git am *.mbx

# 패치 시리즈 정보 조회
b4 patchwork <message-id>

# b4로 패치 전송 준비
b4 prep --fork-point origin/master
b4 prep --edit-cover
b4 send

git bisect (회귀 추적)

특정 커밋에서 버그가 도입된 시점을 이진 탐색으로 찾습니다. 수천 개의 커밋 중에서도 log₂(N) 번의 테스트로 원인 커밋을 특정할 수 있습니다.

# bisect 시작
git bisect start

# 현재(버그 있음) = bad, 정상 동작 커밋 = good
git bisect bad HEAD
git bisect good v6.6

# Git이 중간 커밋을 체크아웃 → 테스트 → good/bad 반복
# 빌드 & 테스트
make -j$(nproc) && qemu-system-x86_64 ...
git bisect good   # 또는 git bisect bad

# 자동 bisect (스크립트로 자동화)
git bisect start HEAD v6.6
git bisect run ./test-script.sh

# bisect 종료 & 정리
git bisect reset
Git 커널 패치 워크플로 1. 코드 수정 git checkout -b fix vim drivers/... make -j$(nproc) 테스트 통과 확인 2. 커밋 작성 git add ... git commit -s Signed-off-by: 필수 50자 제목 + 72자 본문 3. 스타일 검사 checkpatch.pl sparse (C=1) coccicheck 경고 0개 목표 4. 패치 생성 format-patch -1 get_maintainer.pl 수신자 결정 커버 레터 작성 5. 전송 git send-email 또는 b4 send 메일링 리스트 메인테이너 CC 리뷰 사이클 메인테이너 리뷰 Reviewed-by: / Acked-by: 수정 요청 (NAK) 수정 & 재전송 (v2+) format-patch -v2 변경 로그 기록 승인 & 머지 메인테이너 트리에 적용 → linux-next → mainline 릴리스 머지 윈도우 (2주) → rc1 → ... → 릴리스 커널 커밋 메시지 형식 subsystem: Brief summary under 50 chars Detailed explanation... (72 chars/line) | Signed-off-by: Name <email> | Fixes: SHA1 ("original commit")
그림: Git 커널 패치 워크플로 - 코드 수정부터 업스트림 머지까지
커밋 메시지 규칙:
  • 제목: subsystem: 변경 요약 (50자 이내, 마침표 없음)
  • 본문: 왜 변경이 필요한지 설명 (72자/줄)
  • Signed-off-by: DCO(Developer Certificate of Origin) 동의 필수 (git commit -s)
  • Fixes: 버그 수정 시 원인 커밋 SHA 참조
  • Cc: stable 백포트 요청 시 Cc: stable@vger.kernel.org

트러블슈팅 플레이북

아래 순서대로 점검하면 환경 문제를 빠르게 축소할 수 있습니다. 핵심은 문제 범위를 한 단계씩 좁히는 것입니다.

  1. 도구 문제 분리: gcc --version, make --version, ld --version
  2. 설정 문제 분리: make mrproper && make defconfig로 최소 상태 확인
  3. 소스 문제 분리: 같은 커밋을 깨끗한 트리에서 다시 빌드
  4. 런타임 문제 분리: QEMU에서 재현되는지 먼저 확인
  5. 디버깅 단계 진입: GDB 브레이크포인트와 부팅 로그를 함께 확보
오류 메시지 예시 우선 점검 대응
No rule to make target ... 빌드 트리 오염 여부 make mrproper 후 defconfig부터 재시작
fatal error: openssl/... not found 개발 헤더 누락 libssl-dev 또는 openssl-devel 설치
pahole not found dwarves 패키지 설치 여부 dwarves/pahole 설치 후 재빌드
undefined reference ... 툴체인/설정 불일치 ARCH/CROSS_COMPILE/CONFIG 조합 재확인
QEMU 패닉 후 즉시 종료 로그 확보 실패 -nographic + panic=-1 + 로그 파일 저장
로그 수집 최소 세트: 빌드 로그(build.log), QEMU 부팅 로그(qemu-boot.log), 커널 설정(.config), 커밋 해시를 항상 함께 보관하세요. 이 네 가지가 있으면 대부분의 환경 문제를 재현하고 분석할 수 있습니다.

고급 트러블슈팅

오류 메시지 / 증상 원인 해결
BTF: .tmp_vmlinux.btf: pahole ... not found CONFIG_DEBUG_INFO_BTF 활성화 + pahole 미설치 sudo apt install dwarves 또는 CONFIG_DEBUG_INFO_BTF=n
zstd: command not found 모듈 압축에 zstd 필요 sudo apt install zstd
GDB Remote 'g' packet reply is too long GDB 아키텍처 불일치 set arch i386:x86-64 또는 올바른 gdb-multiarch 사용
clangd compile_commands.json not found 컴파일 DB 미생성 ./scripts/clang-tools/gen_compile_commands.py 실행
ccache cache miss 비율 높음 설정 변경, 캐시 크기 부족 ccache -M 20G, KBUILD_BUILD_TIMESTAMP 고정
QEMU Could not access KVM kernel module KVM 모듈 미로드 또는 권한 부족 sudo modprobe kvm_intel, sudo usermod -aG kvm $USER
KASAN BUG: KASAN: slab-out-of-bounds 버퍼 오버플로 감지 보고된 스택 트레이스에서 접근 위치 확인 후 경계 검사 추가
LOCKDEP possible circular locking 데드락 위험 감지 락 획득 순서 재검토, 보고된 체인 분석
Kernel panic - not syncing: Attempted to kill init! PID 1(init) 프로세스 종료 initramfs의 init 스크립트가 exec /bin/sh로 끝나는지 확인
No working init found init 실행 파일 없음 rdinit=/init 파라미터 확인, init에 실행 권한(chmod +x) 확인

환경 진단 스크립트

#!/bin/bash
# kernel-env-check.sh - 커널 개발 환경 진단

echo "=== 커널 개발 환경 진단 ==="
echo

# 필수 도구
echo "[필수 도구]"
for cmd in gcc make git flex bison bc; do
  if command -v $cmd >/dev/null 2>&1; then
    echo "  ✓ $cmd: $($cmd --version 2>&1 | head -1)"
  else
    echo "  ✗ $cmd: 미설치"
  fi
done

# 라이브러리
echo
echo "[필수 라이브러리]"
for lib in libelf openssl; do
  if pkg-config --exists $lib 2>/dev/null; then
    echo "  ✓ $lib: $(pkg-config --modversion $lib)"
  else
    echo "  ✗ $lib: 미설치 또는 dev 패키지 필요"
  fi
done

# 선택 도구
echo
echo "[선택 도구]"
for cmd in clangd ctags cscope qemu-system-x86_64 gdb ccache sparse; do
  if command -v $cmd >/dev/null 2>&1; then
    echo "  ✓ $cmd"
  else
    echo "  - $cmd: 미설치 (선택)"
  fi
done

# KVM
echo
echo "[KVM 지원]"
if [ -e /dev/kvm ]; then
  echo "  ✓ /dev/kvm 존재"
  if [ -r /dev/kvm ] && [ -w /dev/kvm ]; then
    echo "  ✓ 현재 사용자 접근 가능"
  else
    echo "  ✗ 권한 부족: sudo usermod -aG kvm \$USER"
  fi
else
  echo "  ✗ /dev/kvm 없음: BIOS에서 가상화 활성화 필요"
fi

# 디스크/메모리
echo
echo "[시스템 리소스]"
echo "  RAM: $(free -h | awk '/Mem:/{print $2}')"
echo "  디스크 여유: $(df -h . | awk 'NR==2{print $4}')"
echo "  CPU 코어: $(nproc)"

추가 팁

디스크 공간: 커널 소스는 약 3GB, 빌드 산출물은 10GB 이상 차지합니다. SSD에 충분한 공간을 확보하세요.
RAM: 최소 8GB, 권장 16GB 이상. 병렬 빌드(-j 옵션)는 메모리를 많이 소비합니다. OOM 발생 시 병렬도를 낮추세요.
커널 버전 관리: 여러 커널 버전을 동시에 개발하는 경우, git worktree를 사용하면 편리합니다.
git worktree add ../linux-stable stable
git worktree add ../linux-next next
빠른 부팅 테스트: initramfs를 사용하면 별도의 rootfs 이미지 없이 빠르게 테스트할 수 있습니다.
./scripts/config --enable BLK_DEV_INITRD
make -j$(nproc)
qemu-system-x86_64 -kernel arch/x86/boot/bzImage -append "console=ttyS0" -nographic

Docker/Podman 컨테이너 개발 환경

컨테이너 기반 개발 환경은 호스트 시스템을 오염시키지 않으면서 재현 가능한 빌드 환경을 제공합니다. 팀 전체가 동일한 툴체인 버전을 사용하도록 강제할 수 있어, "내 머신에서는 빌드되는데" 문제를 완전히 차단합니다.

컨테이너 개발의 장점

항목 호스트 직접 설치 컨테이너 환경
재현성 호스트 업데이트에 따라 깨질 수 있음 Dockerfile 고정으로 완전 재현
다중 툴체인 버전 충돌 위험 이미지별 독립 환경
정리 패키지 잔여물 누적 컨테이너 삭제로 깔끔 정리
CI 연동 CI와 로컬 환경 불일치 동일 이미지로 CI/로컬 통일
크로스 컴파일 복잡한 멀티 아키텍처 설정 아키텍처별 전용 이미지

커널 빌드용 Dockerfile

# Dockerfile.kernel-dev
FROM ubuntu:24.04

LABEL maintainer="kernel-dev"
LABEL description="Linux kernel development environment"

# 비대화형 설치
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Seoul

# 필수 빌드 도구
RUN apt-get update && apt-get install -y \
    build-essential gcc g++ make \
    git flex bison \
    libelf-dev libssl-dev \
    bc libncurses-dev \
    cpio rsync kmod \
    dwarves sparse ccache \
    \
    # 코드 탐색
    universal-ctags cscope \
    clangd-18 clang-18 lld-18 llvm-18 \
    \
    # 가상화 & 디버깅
    qemu-system-x86 qemu-system-arm \
    gdb gdb-multiarch \
    \
    # 크로스 컴파일
    gcc-aarch64-linux-gnu \
    gcc-arm-linux-gnueabihf \
    gcc-riscv64-linux-gnu \
    \
    # 유틸리티
    vim tmux ripgrep \
    python3 python3-pip \
    curl wget sudo \
    coccinelle \
    \
    && rm -rf /var/lib/apt/lists/*

# ccache 설정
RUN ccache -M 20G
ENV PATH="/usr/lib/ccache:${PATH}"

# 비루트 사용자 생성
ARG USER=kdev
ARG UID=1000
RUN useradd -m -u ${UID} -s /bin/bash ${USER} \
    && echo "${USER} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

USER ${USER}
WORKDIR /home/${USER}/linux

CMD ["/bin/bash"]

컨테이너 빌드 및 실행

# 이미지 빌드
docker build -t kernel-dev -f Dockerfile.kernel-dev .

# 커널 소스를 마운트하여 실행
docker run -it --rm \
  -v $(pwd)/linux:/home/kdev/linux \
  -v kernel-ccache:/home/kdev/.cache/ccache \
  --device /dev/kvm \
  --name kernel-build \
  kernel-dev

# Podman 사용 시 (rootless)
podman run -it --rm \
  -v $(pwd)/linux:/home/kdev/linux:Z \
  -v kernel-ccache:/home/kdev/.cache/ccache:Z \
  --device /dev/kvm \
  --userns=keep-id \
  kernel-dev

# 컨테이너 안에서 빌드
make defconfig
make -j$(nproc)

# 컨테이너 안에서 QEMU 테스트
qemu-system-x86_64 \
  -kernel arch/x86/boot/bzImage \
  -append "console=ttyS0" \
  -nographic -enable-kvm -m 2G
볼륨 마운트 전략:
  • 소스 코드: 호스트의 커널 소스를 바인드 마운트 → 에디터는 호스트에서, 빌드는 컨테이너에서
  • ccache 캐시: named volume으로 컨테이너 재생성에도 캐시 유지
  • /dev/kvm: KVM 가속을 위해 디바이스 전달
  • 빌드 산출물: 소스 바인드 마운트에 포함되므로 호스트에서도 접근 가능

아키텍처별 컨테이너 전략

# ARM64 크로스 빌드 전용 컨테이너
docker run -it --rm \
  -v $(pwd)/linux:/home/kdev/linux \
  -e ARCH=arm64 \
  -e CROSS_COMPILE=aarch64-linux-gnu- \
  kernel-dev bash -c "make defconfig && make -j\$(nproc)"

# docker-compose.yml로 다중 아키텍처 동시 빌드
# docker compose up --parallel
# docker-compose.yml
services:
  x86-build:
    image: kernel-dev
    volumes:
      - ./linux:/home/kdev/linux
      - ccache-x86:/home/kdev/.cache/ccache
    command: bash -c "make x86_64_defconfig && make -j$(nproc)"

  arm64-build:
    image: kernel-dev
    volumes:
      - ./linux:/home/kdev/linux
      - ccache-arm64:/home/kdev/.cache/ccache
    environment:
      - ARCH=arm64
      - CROSS_COMPILE=aarch64-linux-gnu-
    command: bash -c "make defconfig && make -j$(nproc)"

  riscv-build:
    image: kernel-dev
    volumes:
      - ./linux:/home/kdev/linux
      - ccache-riscv:/home/kdev/.cache/ccache
    environment:
      - ARCH=riscv
      - CROSS_COMPILE=riscv64-linux-gnu-
    command: bash -c "make defconfig && make -j$(nproc)"

volumes:
  ccache-x86:
  ccache-arm64:
  ccache-riscv:
Docker 기반 커널 개발 환경 아키텍처 호스트 시스템 에디터 (호스트) VS Code / Vim clangd / ctags 코드 편집 커널 소스 /home/user/linux/ 바인드 마운트 ↕ 공유 /dev/kvm 하드웨어 가속 디바이스 전달 ccache 볼륨 named volume 영구 캐시 Git 버전 관리 호스트 실행 Docker / Podman 컨테이너 x86_64 빌드 컨테이너 GCC 14 Make / binutils GDB QEMU x86 make defconfig && make -j$(nproc) → bzImage + vmlinux QEMU 부팅 테스트 + GDB 디버깅 ARM64 크로스 빌드 컨테이너 aarch64-gnu-gcc gdb-multiarch QEMU aarch64 DTB 도구 ARCH=arm64 CROSS_COMPILE=aarch64-... → Image (ARM64) QEMU -M virt -cpu cortex-a57 RISC-V 크로스 빌드 컨테이너 riscv64-gnu-gcc gdb-multiarch QEMU riscv64 OpenSBI ARCH=riscv CROSS_COMPILE=riscv64-... → Image (RISC-V) QEMU -M virt (RISC-V) 네이티브 빌드 크로스 빌드 크로스 빌드
그림: Docker 기반 커널 개발 환경 아키텍처 - 호스트에서 편집, 컨테이너에서 빌드/테스트
주의사항:
  • UID 매핑: 컨테이너 내부 UID와 호스트 UID가 다르면 소스 파일 권한 문제 발생 → --build-arg UID=$(id -u) 사용
  • 빌드 산출물 소유권: 컨테이너에서 생성한 파일의 소유자가 호스트와 다를 수 있음 → Podman의 --userns=keep-id 권장
  • SELinux: Fedora/RHEL에서는 바인드 마운트에 :Z 접미사 필요

원격 개발 환경

커널 개발은 고성능 머신이 필요하므로, 원격 서버에서 빌드하고 로컬에서 편집하는 패턴이 흔합니다. SSH, tmux, VS Code Remote를 활용하면 원격 환경에서도 쾌적한 개발이 가능합니다.

SSH 설정 최적화

# ~/.ssh/config

# 커널 빌드 서버
Host kernel-build
    HostName 192.168.1.100
    User kdev
    Port 22
    IdentityFile ~/.ssh/id_ed25519
    # 연결 유지
    ServerAliveInterval 60
    ServerAliveCountMax 3
    # 멀티플렉싱 (연결 재사용)
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600
    # 압축
    Compression yes
    # GDB 포트 포워딩
    LocalForward 1234 localhost:1234

tmux 기본 설정

원격 환경에서 tmux는 필수입니다. SSH 연결이 끊어져도 빌드가 계속 실행되며, 여러 터미널을 동시에 관리할 수 있습니다.

# ~/.tmux.conf

# 커널 개발 전용 설정
set -g default-terminal "screen-256color"
set -g history-limit 50000
set -g mouse on

# 프리픽스 키 변경 (선택)
set -g prefix C-a
unbind C-b
bind C-a send-prefix

# 창 분할 키 바인딩
bind | split-window -h
bind - split-window -v

# 상태바에 호스트명 표시
set -g status-left "[#S@#H] "
set -g status-right "%H:%M %d-%b"
# 커널 개발 세션 스크립트
#!/bin/bash
# kernel-tmux.sh - 커널 개발 tmux 세션 자동 생성

SESSION="kernel"
LINUX_DIR="$HOME/linux"

tmux new-session -d -s $SESSION -c $LINUX_DIR

# 윈도우 0: 에디터
tmux rename-window -t $SESSION:0 "edit"
tmux send-keys -t $SESSION:0 "vim ." C-m

# 윈도우 1: 빌드
tmux new-window -t $SESSION -n "build" -c $LINUX_DIR

# 윈도우 2: QEMU (수평 분할)
tmux new-window -t $SESSION -n "qemu" -c $LINUX_DIR
tmux split-window -h -t $SESSION:2 -c $LINUX_DIR
# 왼쪽: QEMU 실행, 오른쪽: GDB 연결

# 윈도우 3: 로그 & 모니터링
tmux new-window -t $SESSION -n "logs" -c $LINUX_DIR

tmux select-window -t $SESSION:1
tmux attach -t $SESSION

VS Code Remote Development

VS Code의 Remote-SSH 확장을 사용하면 로컬에서 편집하면서 원격 서버의 clangd, 빌드 시스템을 그대로 활용할 수 있습니다.

// 원격 서버의 .vscode/settings.json
{
    "remote.SSH.remotePlatform": {
        "kernel-build": "linux"
    },
    "clangd.path": "/usr/bin/clangd-18",
    "clangd.arguments": [
        "--background-index",
        "--clang-tidy",
        "--completion-style=detailed",
        "-j=4"
    ],
    "files.watcherExclude": {
        "**/.git/objects/**": true,
        "**/build/**": true
    },
    // 빌드 태스크
    "tasks.version": "2.0.0"
}
// .vscode/tasks.json - 커널 빌드 태스크
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Kernel Build",
            "type": "shell",
            "command": "make -j$(nproc)",
            "group": { "kind": "build", "isDefault": true },
            "problemMatcher": "$gcc"
        },
        {
            "label": "Kernel Boot (QEMU)",
            "type": "shell",
            "command": "qemu-system-x86_64 -kernel arch/x86/boot/bzImage -append 'console=ttyS0 nokaslr' -nographic -enable-kvm -m 2G -s -S",
            "isBackground": true
        },
        {
            "label": "checkpatch",
            "type": "shell",
            "command": "./scripts/checkpatch.pl --file ${file}",
            "problemMatcher": []
        }
    ]
}
원격 개발 최적화 팁:
  • SSH 멀티플렉싱: ControlMaster auto로 연결 재사용 → VS Code 재연결 속도 대폭 향상
  • 파일 감시 제외: .git/objects, build/ 디렉토리를 감시 대상에서 제외하여 CPU 절약
  • GDB 포트 포워딩: SSH config에 LocalForward 1234 설정 → 로컬 VS Code에서 원격 QEMU에 GDB 연결
  • tmux + SSH: 장시간 빌드 시 SSH 끊김에도 빌드 계속 진행

커널 설정 전략 심화

커널 설정(.config)은 4000개 이상의 옵션으로 구성됩니다. 목적에 맞는 설정 전략을 세우면 빌드 시간 단축과 디버깅 효율 향상을 동시에 달성할 수 있습니다.

설정 도구 비교

명령 인터페이스 의존성 특징
make menuconfig ncurses TUI libncurses-dev 가장 보편적, 검색(/) 지원
make nconfig ncurses TUI (개선판) libncurses-dev 향상된 검색, F1~F9 키
make xconfig Qt5 GUI qtbase5-dev 트리 뷰, 의존성 시각화
make gconfig GTK GUI libgtk-3-dev GTK 기반 대안
make oldconfig CLI (질문) 없음 기존 .config에 새 옵션만 질문
make olddefconfig CLI (자동) 없음 새 옵션은 기본값으로 자동 설정
./scripts/config CLI (스크립트) 없음 개별 옵션 enable/disable, 자동화에 적합

목적별 설정 전략

커널 설정 전략 플로차트 목적이 무엇인가? 빠른 학습/실험 make tinyconfig → 최소 옵션 (500개 미만) → 빌드 1~2분 → 부팅 불가, 학습 전용 make allnoconfig → 모든 옵션 비활성화 기반 개발/디버깅 make defconfig → 아키텍처 기본 설정 → 빌드 5~15분 → QEMU 부팅 가능 + CONFIG_DEBUG_INFO + CONFIG_GDB_SCRIPTS 배포/하드웨어 대응 make localmodconfig → 현재 로드된 모듈 기반 → 불필요 드라이버 제거 → 빌드 시간 50% 절감 make localyesconfig → 모듈 대신 빌트인 전체 커버리지 테스트 make allyesconfig → 모든 옵션 활성화 → 빌드 30분~1시간+ → 컴파일 에러 검출용 make allmodconfig → 가능한 모든 것을 모듈로 ./scripts/config를 활용한 자동화 예시 ./scripts/config --enable DEBUG_INFO # 개별 옵션 활성화 ./scripts/config --disable MODULE_SIG # 개별 옵션 비활성화 ./scripts/config --set-val NR_CPUS 4 # 정수 값 설정 ./scripts/config --set-str LOCALVERSION "-mykernel" # 문자열 값 설정 ./scripts/config --module BTRFS_FS # 모듈로 설정 (=m) ./scripts/config --state DEBUG_INFO # 현재 상태 조회
그림: 커널 설정 전략 플로차트 - 목적에 따른 최적 설정 방법 선택

설정 프래그먼트 관리

프로젝트별 설정 변경을 .config 직접 수정 대신 프래그먼트 파일로 관리하면 버전 관리와 재현이 쉬워집니다.

# 디버그 프래그먼트: debug.config
cat > debug.config <<'EOF'
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_GDB_SCRIPTS=y
CONFIG_FRAME_POINTER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_FS=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_LOCKDEP=y
CONFIG_PROVE_LOCKING=y
CONFIG_DEBUG_ATOMIC_SLEEP=y
CONFIG_KASAN=y
EOF

# 프래그먼트 적용: defconfig + 디버그 옵션
cd /path/to/linux
make defconfig
./scripts/kconfig/merge_config.sh .config debug.config

# 또는 KCONFIG_ALLCONFIG 사용
make KCONFIG_ALLCONFIG=debug.config alldefconfig
# 최소 QEMU 부팅 프래그먼트: qemu-minimal.config
cat > qemu-minimal.config <<'EOF'
CONFIG_PCI=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_NET=y
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM_VIRTIO=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_EXT4_FS=y
CONFIG_TMPFS=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
EOF
설정 관리 베스트 프랙티스:
  • 프래그먼트 파일을 Git에 커밋하여 팀과 공유
  • make savedefconfig로 현재 설정의 최소 diff를 defconfig로 저장
  • scripts/diffconfig로 두 .config 파일의 차이점만 추출
  • CI에서는 merge_config.sh로 베이스 + 프래그먼트 조합 자동화

참고자료