커널 개발 에디터 설정

Vim, VS Code, Emacs, Neovim을 커널 개발에 최적화하는 설정 가이드입니다. compile_commands.json 생성, clangd LSP 통합, 에디터별 디버깅(Debugging) 연동까지 실전 워크플로를 정리합니다.

사전 준비: 이 문서는 커널 빌드 환경이 갖춰진 상태를 전제합니다. 개발 환경 설정에서 gcc, make, git 등 필수 도구를 먼저 설치하세요.
일상 비유: 에디터 설정은 조종석 계기판 배치와 같습니다. 비행기는 같아도 계기판 배치에 따라 조종사의 효율이 달라지듯, 같은 커널 소스라도 에디터가 clangd와 올바르게 연결되면 코드 탐색·편집 속도가 크게 달라집니다.

핵심 요약

  • compile_commands.json — 모든 에디터의 코드 인텔리전스 기반입니다. 커널 빌드 후 반드시 생성해야 합니다.
  • clangd — LSP(Language Server Protocol) 서버로, 정의 이동, 자동완성, 오류 표시 등을 제공합니다.
  • 커널 코딩 스타일(Coding Style) — 탭 너비 8, 탭 문자 사용, 80/100칸 가이드라인을 에디터에 반영합니다.
  • 디버깅 통합 — VS Code와 Neovim은 QEMU+GDB 커널 디버깅을 에디터 내에서 수행할 수 있습니다.
  • 에디터 선택 — 핵심은 에디터 자체가 아니라 clangd + compile_commands.json 조합입니다.

단계별 이해

  1. 커널 빌드
    먼저 커널을 한 번 이상 빌드하여 compile_commands.json을 생성합니다.
  2. clangd 설치
    배포판 패키지로 clangd를 설치하고 .clangd 설정 파일을 작성합니다.
  3. 에디터 연동
    선호 에디터에 LSP 클라이언트를 설정하여 clangd와 연결합니다.
  4. 워크플로 최적화
    빌드 태스크(Task), 디버깅 설정, 코드 탐색 단축키를 구성합니다.

개요

커널 소스는 약 3만 개의 C 파일, 2,800만 줄 이상의 코드로 구성됩니다. 이 거대한 코드베이스를 효율적으로 탐색하려면 에디터와 언어 서버(Language Server)의 연동이 필수입니다. 모든 에디터의 핵심 흐름은 동일합니다.

Vim / Neovim VS Code Emacs LSP 클라이언트 내장 LSP 프로토콜 (JSON-RPC) clangd Language Server 정의 이동 · 자동완성 · 진단 리팩토링 · 호버 정보 읽기 compile_commands.json 파일별 컴파일 명령어 DB include 경로 · 매크로(Macro) · 플래그 생성 make / bear / compiledb 커널 빌드 시 자동 생성 커널 소스 트리 (~30,000 .c) ctags / cscope (레거시 대안) clangd 없는 환경에서 사용

위 구조에서 가장 중요한 파일은 compile_commands.json입니다. 이 파일이 없으면 clangd가 커널 소스의 include 경로와 매크로 정의를 알 수 없어, 에디터의 코드 인텔리전스가 동작하지 않습니다.

커널 개발 워크플로

에디터 설정의 궁극적인 목표는 편집→빌드→테스트→디버그 사이클을 에디터 내에서 빠르게 반복하는 것입니다. 아래 다이어그램은 커널 개발의 전형적인 워크플로를 보여줍니다.

커널 개발 워크플로 사이클 1. 편집 clangd 자동완성/진단 정의 이동, 참조 검색 2. 스타일 검사 checkpatch.pl 코딩 스타일 위반 확인 3. 빌드 make -j$(nproc) 오류 → 소스 위치 이동 4. 테스트 QEMU 부팅 / 모듈 로드 kselftest / kunit 5. 디버그 QEMU + GDB 연동 브레이크포인트, 스택 추적 6. 커밋 git commit / format-patch checkpatch --strict 최종 검사 다음 사이클 에디터 내 통합: LSP (편집) + Task (빌드/검사) + DAP (디버그) + Git 확장 (커밋)
워크플로 단축키: 각 단계를 에디터 단축키로 연결하면 효율이 극대화됩니다. VS Code: Ctrl+Shift+B (빌드) → F5 (디버그) → Ctrl+Shift+G (Git). Vim/Neovim: :make (빌드) → :cn (다음 오류) → :Telescope lsp_references (참조 검색).

compile_commands.json 생성과 관리

compile_commands.json은 각 소스 파일의 컴파일 명령어를 JSON 배열로 기록한 데이터베이스입니다. clangd는 이 파일을 통해 -I include 경로, -D 매크로 정의, 타겟 아키텍처 등을 파악합니다.

생성 방법

방법명령어요구 사항특징
커널 내장make compile_commands.json커널 5.x+가장 권장, 정확도 최고
gen_compile_commands.pyscripts/clang-tools/gen_compile_commands.py커널 5.x+빌드 후 .cmd 파일에서 추출
bearbear -- make -j$(nproc)bear 패키지빌드 래핑, 모든 프로젝트 범용
compiledbcompiledb make -j$(nproc)pip install compiledbPython 기반, make 출력 파싱
# 방법 1: 커널 내장 (권장)
make defconfig
make -j$(nproc)
make compile_commands.json

# 방법 2: gen_compile_commands.py (빌드 완료 후)
python3 scripts/clang-tools/gen_compile_commands.py

# 방법 3: bear (외부 도구)
sudo apt install bear
bear -- make -j$(nproc)

# 생성 확인
ls -lh compile_commands.json
jq 'length' compile_commands.json   # 항목 수 확인 (수천~수만)
jq '.[0]' compile_commands.json      # 첫 항목 구조 확인
코드 설명
  • 2-4행 커널 내장 방식: 먼저 일반 빌드를 수행한 후 make compile_commands.json을 실행하면 빌드 과정에서 기록된 .cmd 파일들을 취합하여 JSON 데이터베이스를 생성합니다.
  • 10행 bear는 빌드 명령을 래핑하여 실제 컴파일러 호출을 가로채고 기록합니다. 커널 외 프로젝트에서도 사용 가능합니다.
  • 13-14행 jq로 생성된 파일을 검증합니다. defconfig 기준으로 보통 15,000~20,000개의 항목이 생성됩니다.

크로스 컴파일(Cross Compilation) 환경

크로스 컴파일 커널의 compile_commands.json을 생성할 때는 clangd가 호스트 컴파일러가 아닌 크로스 컴파일러의 include 경로를 찾을 수 있도록 추가 설정이 필요합니다.

# ARM64 크로스 컴파일 후 compile_commands.json 생성
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- compile_commands.json

자주 발생하는 문제

증상원인해결
clangd "unknown argument" 경고GCC 전용 플래그 (-mrecord-mcount 등).clangd에서 Remove:로 제거
include 파일을 찾지 못함compile_commands.json 미생성 또는 위치 불일치커널 루트에 파일 존재 확인
오래된 진단 결과커널 설정 변경 후 재생성 안 함make compile_commands.json 재실행
항목이 비어 있음 (length=0)빌드 없이 생성 시도먼저 make -j$(nproc) 실행 후 생성
크로스 컴파일 include 오류호스트 경로로 헤더 검색.clangd--query-driver 설정

clangd 공통 설정

clangd는 프로젝트 루트의 .clangd 파일로 동작을 제어합니다. 커널처럼 거대한 코드베이스에서는 메모리 최적화와 GCC 전용 플래그 처리가 중요합니다.

.clangd 설정 파일

# 커널 소스 루트에 .clangd 파일 생성

CompileFlags:
  # GCC 전용 플래그 제거 (clangd가 인식 못 하는 옵션)
  Remove:
    - -mpreferred-stack-boundary=*
    - -mindirect-branch=*
    - -mindirect-branch-register
    - -mindirect-branch-cs-prefix
    - -mfunction-return=*
    - -mrecord-mcount
    - -mskip-rax-setup
    - -fno-allow-store-data-races
    - -fconserve-stack
    - -ftrivial-auto-var-init=*
  # 크로스 컴파일 시 toolchain 경로 지정
  # Compiler: /usr/bin/aarch64-linux-gnu-gcc

Index:
  Background: Build   # 백그라운드 인덱싱 (Build/Skip)

Diagnostics:
  Suppress:
    - drv_unknown_argument   # 알 수 없는 컴파일러 플래그 경고 억제
코드 설명
  • 5-14행 Remove: 섹션은 clang이 지원하지 않는 GCC 전용 플래그를 제거합니다. 커널은 기본적으로 GCC로 빌드되므로 compile_commands.json에 GCC 전용 옵션이 포함됩니다.
  • 19행 Background: Build는 백그라운드에서 전체 프로젝트를 인덱싱합니다. 초기 인덱싱에 수 분이 걸리지만, 이후 심볼 검색과 정의 이동이 빨라집니다.
  • 22행 drv_unknown_argument 억제는 GCC 전용 플래그를 완전히 제거하지 못한 경우의 경고를 숨깁니다.

커널 코드베이스 성능 최적화

커널 소스는 일반 프로젝트 대비 매우 크므로 clangd의 메모리 사용량과 인덱싱 시간에 주의해야 합니다.

옵션효과권장 값
-j=N병렬 인덱싱 스레드(Thread) 수CPU 코어의 절반 (예: 8코어 → -j=4)
--malloc-trim미사용 메모리 OS에 반환메모리 부족 시 활성화
--background-index-priority=low인덱싱 우선순위(Priority) 낮춤편집 중 응답성 향상
--pch-storage=memoryPCH를 메모리에 유지RAM 16GB 이상 시 활성화
--completion-style=detailed자동완성 항목에 타입 정보 포함항상 활성화
메모리 사용량 기준: defconfig 기준 clangd 인덱싱 완료 후 약 2~4GB의 RAM을 사용합니다. allyesconfig의 경우 8GB 이상이 필요할 수 있습니다. 메모리가 부족하면 -j=2로 병렬도를 낮추세요.

크로스 컴파일 환경에서의 clangd

크로스 컴파일러의 시스템 헤더 경로를 clangd에 알려줘야 합니다. --query-driver 옵션이 이 역할을 합니다.

# 에디터의 clangd 실행 인수에 추가
clangd --query-driver="/usr/bin/aarch64-linux-gnu-*" \
       --background-index \
       -j=4

--query-driver는 지정된 패턴에 매칭되는 컴파일러를 실행하여 시스템 include 경로를 자동으로 가져옵니다. 이를 통해 <linux/module.h> 같은 커널 헤더의 크로스 환경 경로가 올바르게 해석됩니다.

Vim

Vim은 커널 개발자들 사이에서 가장 인기 있는 에디터 중 하나입니다. 가벼운 리소스 사용, SSH 환경에서의 즉시 사용, cscope/ctags와의 오랜 통합 역사가 강점입니다.

심층 가이드: Vim의 모달 편집, 문법 체계, 레지스터(Register), 매크로 등은 Vim 완벽 가이드를 참고하세요. 이 섹션에서는 커널 개발에 필요한 설정만 다룹니다.

.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

ctags/cscope 인덱싱 흐름

clangd가 없는 환경(SSH 서버, 임베디드 장비)에서는 ctags와 cscope가 여전히 유용합니다. 아래 다이어그램은 인덱싱과 코드 탐색 흐름을 보여줍니다.

ctags / cscope 인덱싱과 탐색 커널 소스 트리 ~30,000 .c / .h 파일 make tags tags 파일 심볼→파일:줄번호 매핑 make cscope cscope.out 교차 참조 DB (호출/피호출) Vim / Emacs 코드 탐색 명령어 ctags vs cscope 기능 비교 ctags ✓ 정의(definition) 이동 ✓ 빠른 속도 ✗ 참조/호출 검색 불가 cscope ✓ 정의 + 참조 + 호출 검색 ✓ 텍스트 패턴(Pattern) 검색 ✗ ctags보다 느림 clangd (LSP) ✓ 전체 기능 + 자동완성 ✓ 의미 기반 정확한 분석 ✗ 메모리 사용량 높음

vim-gutentags: 태그 자동 관리

vim-gutentags 플러그인은 파일 저장 시 ctags/cscope 데이터베이스를 백그라운드에서 자동 갱신합니다. 수동으로 make tagsmake cscope를 실행할 필요가 없어집니다.

" vim-gutentags 설정 (.vimrc에 추가)
let g:gutentags_project_root = ['Makefile', 'Kconfig']
let g:gutentags_ctags_tagfile = '.tags'
let g:gutentags_modules = ['ctags', 'cscope']
let g:gutentags_cache_dir = expand('~/.cache/tags')
let g:gutentags_ctags_extra_args = ['--fields=+niazS', '--extras=+q']

CoC.nvim: Vim에서 LSP 사용

Vim 8.2+에서 clangd를 사용하려면 coc.nvim(Conquer of Completion) 플러그인이 효과적입니다. Vim에 내장 LSP가 없으므로 이 플러그인이 LSP 클라이언트 역할을 합니다.

// ~/.vim/coc-settings.json
{
    "clangd.path": "/usr/bin/clangd",
    "clangd.arguments": [
        "--background-index",
        "--clang-tidy",
        "--completion-style=detailed",
        "--header-insertion=never",
        "-j=4"
    ]
}

cscope/ctags 빠른 참조

작업ctagscscope
정의로 이동Ctrl+]Ctrl+\ g
뒤로 돌아가기Ctrl+tCtrl+o
심볼 검색Ctrl+\ s
호출하는 함수 찾기Ctrl+\ c
호출되는 함수 찾기Ctrl+\ d
텍스트 검색Ctrl+\ t
데이터베이스 생성make tagsmake cscope

Visual Studio Code

VS Code는 풍부한 확장 생태계와 직관적인 UI, 내장 디버거를 제공합니다. 특히 Remote-SSH를 통한 원격 개발과 QEMU+GDB 커널 디버깅 통합이 강력합니다.

필수 확장 설치

확장기능비고
clangdLSP 기반 인텔리센스, 코드 탐색, 리팩토링핵심 확장, 최우선 설치
C/C++ (Microsoft)디버깅(GDB/LLDB), IntelliSenseclangd와 IntelliSense 충돌 주의
GitLensGit blame, 히스토리 탐색커밋 이력 추적에 유용
Remote - SSH원격 서버에서 직접 편집원격 빌드 서버 활용 시 필수
Hex Editor바이너리 파일 확인vmlinux, .ko 파일 검사

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",
    "clangd.arguments": [
        "--background-index",
        "--clang-tidy",
        "--completion-style=detailed",
        "--header-insertion=never",
        "-j=4"
    ],

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

    // ===== 파일 감시 제외 (성능 최적화) =====
    "files.watcherExclude": {
        "**/.git/objects/**": true,
        "**/build/**": true,
        "**/.tmp_versions/**": true
    },
    "search.exclude": {
        "**/build/**": true,
        "**/.tmp_versions/**": true
    }
}
clangd와 C/C++ 확장 충돌: 두 확장을 함께 사용하면 자동완성이 중복됩니다. "C_Cpp.intelliSenseEngine": "disabled"로 Microsoft IntelliSense를 끄되, C/C++ 확장의 디버깅 기능은 유지됩니다.

tasks.json: 빌드 태스크

// .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 Build (Clang)",
            "type": "shell",
            "command": "make CC=clang -j$(nproc)",
            "problemMatcher": "$gcc"
        },
        {
            "label": "checkpatch",
            "type": "shell",
            "command": "./scripts/checkpatch.pl --file ${file}",
            "problemMatcher": []
        },
        {
            "label": "Regenerate compile_commands.json",
            "type": "shell",
            "command": "make compile_commands.json",
            "problemMatcher": []
        }
    ]
}

launch.json: QEMU+GDB 커널 디버깅

VS Code에서 QEMU 위의 커널을 소스 레벨로 디버깅할 수 있습니다. QEMU의 GDB 스텁(-s 옵션, 포트 1234)에 연결합니다.

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Kernel Debug (QEMU)",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/vmlinux",
            "miDebuggerServerAddress": "localhost:1234",
            "miDebuggerPath": "/usr/bin/gdb",
            "cwd": "${workspaceFolder}",
            "setupCommands": [
                {
                    "text": "add-auto-load-safe-path ${workspaceFolder}",
                    "ignoreFailures": true
                },
                {
                    "text": "set architecture i386:x86-64",
                    "ignoreFailures": false
                }
            ],
            "stopAtEntry": true
        }
    ]
}
실행 순서:launch.json 예제는 QEMU가 이미 별도 터미널에서 -s -S 옵션으로 실행 중인 상태를 전제로 합니다. 장시간 실행되는 QEMU를 preLaunchTask로 바로 연결하려면 background task 설정을 추가로 맞춰야 하므로, 처음에는 수동 실행이 더 안전합니다.
코드 설명
  • 8행 vmlinux는 비압축 커널 이미지로, GDB 디버그 심볼이 포함됩니다. CONFIG_DEBUG_INFO 활성화가 필수입니다.
  • 9행 QEMU를 -s 옵션으로 실행하면 포트 1234에서 GDB 연결을 대기합니다. -S 옵션은 시작 시 CPU를 정지시킵니다.
  • 13행 커널 소스 디렉토리의 GDB Python 스크립트(scripts/gdb/)를 자동 로드합니다. lx-dmesg, lx-ps 등의 커널 전용 GDB 명령어가 활성화됩니다.

Emacs

Emacs는 오랜 역사를 가진 강력한 에디터로, 커널 개발에 최적화된 다양한 모드를 제공합니다. 특히 TRAMP를 통한 투명한 원격 편집과 compile 모드의 빌드 통합이 강점입니다.

init.el 기본 설정

;; ~/.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")))

eglot: Emacs 29+ 내장 LSP

Emacs 29부터 eglot이 내장되어 별도 패키지 설치 없이 LSP를 사용할 수 있습니다. lsp-mode보다 가볍고 설정이 간단합니다.

;; eglot (Emacs 29+ 내장)
(add-hook 'c-mode-hook 'eglot-ensure)

;; clangd 인수 설정
(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
    '(c-mode . ("clangd"
                "--background-index"
                "--clang-tidy"
                "--completion-style=detailed"
                "--header-insertion=never"
                "-j=4"))))

.dir-locals.el: 프로젝트별 설정

커널 소스 루트에 .dir-locals.el을 배치하면 에디터가 프로젝트에 진입할 때 자동으로 커널 코딩 스타일을 적용합니다.

;; 커널 소스 루트에 .dir-locals.el 배치
((c-mode . ((c-file-style . "linux")
            (indent-tabs-mode . t)
            (tab-width . 8)
            (fill-column . 80)))
 (nil . ((compile-command . "make -j$(nproc)"))))

M-x compile 통합

Emacs의 compile 모드는 빌드 오류를 파싱하여 해당 소스 위치로 바로 이동합니다. 커널 빌드에서 GCC 출력을 자동으로 인식합니다.

;; 빌드 단축키
(global-set-key (kbd "C-c C-k") 'compile)     ;; 빌드 실행
(global-set-key (kbd "C-c C-n") 'next-error)   ;; 다음 오류로 이동
(global-set-key (kbd "C-c C-p") 'previous-error) ;; 이전 오류로 이동

;; TRAMP: 원격 서버에서 커널 빌드
;; C-x C-f /ssh:user@build-server:~/linux/ 로 접속
;; M-x compile → 원격 서버에서 make 실행

Neovim (현대적 Vim)

Neovim은 Vim의 현대적 포크로, 내장 LSP 클라이언트와 treesitter 구문 분석을 기본 제공합니다. 별도의 LSP 플러그인 없이도 clangd를 바로 연동할 수 있습니다.

설치

# 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

내장 LSP 설정 (init.lua)

-- ~/.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) =====
local kernel_root = function(bufnr)
  return vim.fs.root(bufnr, { "compile_commands.json", "Makefile", ".git" })
end

vim.api.nvim_create_autocmd("FileType", {
  pattern = { "c", "cpp" },
  callback = function(args)
    local root = kernel_root(args.buf)
    if not root then
      return
    end

    vim.lsp.start({
      name = "clangd",
      cmd = { "clangd",
        "--background-index",
        "--clang-tidy",
        "--completion-style=detailed",
        "--header-insertion=never",
        "-j=4",
      },
      root_dir = root,
    })
  end,
})

-- ===== 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)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next)

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

-- ===== 진단 표시 =====
vim.diagnostic.config({
  virtual_text = true,
  signs = true,
  underline = true,
})

treesitter: 정확한 구문 강조

treesitter는 정규식 기반 하이라이팅과 달리 AST(Abstract Syntax Tree)를 구축하여 구문을 분석합니다. 커널의 복잡한 매크로(Macro) 체인에서도 정확한 하이라이팅을 제공합니다.

-- lazy.nvim 플러그인 관리자에서 treesitter 설정
{
  "nvim-treesitter/nvim-treesitter",
  build = ":TSUpdate",
  config = function()
    require("nvim-treesitter.configs").setup({
      ensure_installed = { "c", "make", "bash", "lua", "kconfig" },
      highlight = { enable = true },
      indent = { enable = true },
    })
  end,
}

telescope.nvim: 퍼지 검색

커널 소스에서 파일명, 심볼, grep 결과를 빠르게 검색합니다. find_files는 30,000개 이상의 C 파일에서도 즉시 결과를 반환합니다.

-- telescope 키 바인딩 예시
local builtin = require("telescope.builtin")
vim.keymap.set("n", "<leader>ff", builtin.find_files)    -- 파일 검색
vim.keymap.set("n", "<leader>fg", builtin.live_grep)     -- 전체 텍스트 검색
vim.keymap.set("n", "<leader>fs", builtin.lsp_document_symbols) -- LSP 심볼
vim.keymap.set("n", "<leader>fb", builtin.buffers)        -- 열린 버퍼 전환

nvim-dap: 커널 디버깅

DAP(Debug Adapter Protocol)를 통해 Neovim 내에서 QEMU+GDB 커널 디버깅이 가능합니다.

-- nvim-dap GDB 어댑터 설정 (GDB 14+)
local dap = require("dap")
dap.adapters.cppdbg = {
  id = "cppdbg",
  type = "executable",
  command = "gdb",
  args = { "-i", "dap" },
}
dap.configurations.c = {
  {
    name = "Kernel Debug (QEMU)",
    type = "cppdbg",
    request = "launch",
    program = "${workspaceFolder}/vmlinux",
    miDebuggerServerAddress = "localhost:1234",
    cwd = "${workspaceFolder}",
    stopAtEntry = true,
  },
}

에디터 선택 가이드

에디터마다 장점이 다르므로 개발 환경과 워크플로에 따라 선택하면 됩니다. 아래 비교표는 커널 개발 관점에서의 평가입니다.

Vim VS Code Emacs Neovim LSP 지원 CoC/ALE 필요 clangd 확장 eglot 내장 LSP 내장 디버깅 통합 Termdebug GUI 디버거 gdb-mode nvim-dap 원격 개발 SSH 네이티브 Remote-SSH TRAMP SSH 네이티브 학습 곡선 가파름 완만함 매우 가파름 가파름 리소스 사용 최소 (~5MB) 높음 (~500MB) 중간 (~100MB) 최소 (~10MB) 플러그인 생태계 풍부 (Vimscript) 매우 풍부 풍부 (Elisp) 매우 풍부 (Lua) 특히 우수 양호 보통 주의 필요 권장: SSH 서버 → Vim/Neovim | GUI 디버깅 → VS Code | 최대 커스터마이징 → Emacs | 현대적 Vim → Neovim

워크플로별 권장 에디터

워크플로권장 에디터이유
SSH로 원격 서버에서만 작업Vim / Neovim추가 설치 최소, 터미널에서 즉시 사용
GUI 디버거로 커널 디버깅VS Codelaunch.json으로 QEMU+GDB 통합, 브레이크포인트 클릭 설정
원격 서버 + 로컬 GUI 편집VS Code (Remote-SSH)로컬 UI, 원격 clangd/빌드 — 가장 직관적
최대 수준의 커스터마이징EmacsElisp로 모든 동작 제어, Org-mode로 커널 분석 노트
현대적 Vim + LSP 네이티브Neovim내장 LSP, treesitter, Lua 설정, 플러그인 생태계
다양한 에디터를 병행clangd + .clangd에디터 무관하게 동일한 코드 인텔리전스 보장
핵심: 에디터 자체보다 clangd + compile_commands.json 조합이 중요합니다. 이것만 올바르게 설정되면 어떤 에디터든 동일한 코드 인텔리전스를 제공합니다.

EditorConfig: 에디터 공통 설정

EditorConfig는 에디터/IDE 간에 코딩 스타일을 공유하는 표준입니다. 커널 소스 루트에 .editorconfig 파일을 배치하면 Vim, VS Code, Emacs, Neovim 모두 자동으로 커널 코딩 스타일을 적용합니다.

# 커널 소스 루트에 .editorconfig 배치
root = true

# C/ASM 파일: 커널 코딩 스타일
[*.{c,h,S}]
indent_style = tab
indent_size = 8
tab_width = 8
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 80

# Kconfig 파일
[Kconfig*]
indent_style = tab
tab_width = 8

# Makefile
[Makefile*]
indent_style = tab

# Device Tree
[*.{dts,dtsi}]
indent_style = tab
tab_width = 8

# Python 스크립트 (scripts/)
[*.py]
indent_style = space
indent_size = 4
EditorConfig 지원: VS Code(내장), Neovim(내장), Vim(editorconfig-vim 플러그인), Emacs(editorconfig-emacs) 모두 지원합니다. .editorconfig를 한 번 작성하면 팀 전체가 동일한 들여쓰기 규칙을 자동 적용받습니다.

checkpatch.pl 에디터 통합

scripts/checkpatch.pl은 커널 코딩 스타일 준수 여부를 검사하는 공식 도구입니다. 에디터에 통합하면 코드 작성 중에 즉시 스타일 위반을 확인할 수 있습니다.

VS Code에서 checkpatch

// .vscode/tasks.json에 추가
{
    "label": "checkpatch (현재 파일)",
    "type": "shell",
    "command": "./scripts/checkpatch.pl --file --no-tree ${file}",
    "problemMatcher": {
        "pattern": {
            "regexp": "^(WARNING|ERROR):(.+):(\\d+): (.+)$",
            "severity": 1,
            "code": 2,
            "line": 3,
            "message": 4
        }
    },
    "presentation": { "reveal": "always", "panel": "shared" }
}

Vim/Neovim에서 checkpatch

" checkpatch를 :make로 실행
autocmd FileType c setlocal makeprg=./scripts/checkpatch.pl\ --file\ --no-tree\ %
autocmd FileType c setlocal errorformat=%t%*[A-Z]:\ %m:%l:\ %m

" 단축키: <leader>cp → 현재 파일 checkpatch 실행
nnoremap <leader>cp :!./scripts/checkpatch.pl --file --no-tree %<CR>

" git diff에 대해 checkpatch 실행 (커밋 전 검사)
nnoremap <leader>cd :!git diff HEAD~1 \| ./scripts/checkpatch.pl -<CR>

Emacs에서 checkpatch

;; checkpatch.pl을 flymake 또는 flycheck에 연동
(defun kernel-checkpatch ()
  "Run checkpatch.pl on current file."
  (interactive)
  (compile (concat "./scripts/checkpatch.pl --file --no-tree "
                   (buffer-file-name))))

(global-set-key (kbd "C-c C-x") 'kernel-checkpatch)
커밋 전 필수: 패치를 메일링 리스트(Mailing List)에 보내기 전에 반드시 checkpatch.pl --strict를 통과해야 합니다. 사소한 스타일 위반으로 패치가 거부되는 일이 빈번합니다. Git 훅(hook)으로 자동화하는 것을 권장합니다: git diff HEAD~1 | ./scripts/checkpatch.pl -

QEMU+GDB 디버깅 통합 워크플로

에디터 내에서 커널을 소스 레벨로 디버깅하려면 QEMU, GDB, 에디터의 DAP/디버거 인터페이스를 연결해야 합니다. 아래 다이어그램은 전체 연동 구조를 보여줍니다.

QEMU + GDB + 에디터 디버깅 연동 에디터 (UI) 브레이크포인트 설정 변수 감시/스택 추적 Step In/Out/Over VS Code: cppdbg | Neovim: nvim-dap GDB vmlinux 심볼 로드 scripts/gdb/ 자동로드 lx-dmesg, lx-ps, lx-lsmod DAP/MI QEMU -s (GDB stub :1234) -S (시작 시 정지) -kernel bzImage -append "nokaslr" TCP Linux 커널 DEBUG_INFO=y GDB_SCRIPTS=y nokaslr 부트 파라미터 디버그 커널 빌드 설정 (.config) 필수 커널 설정 옵션 CONFIG_DEBUG_INFO=y DWARF 디버그 심볼 포함 CONFIG_GDB_SCRIPTS=y lx-* GDB 헬퍼 명령어 CONFIG_RANDOMIZE_BASE=n KASLR 비활성화 (또는 nokaslr) CONFIG_DEBUG_INFO_DWARF5=y CONFIG_FRAME_POINTER=y CONFIG_KGDB=y (선택)
# 1. 디버그 커널 빌드
make defconfig
./scripts/config -e DEBUG_INFO -e DEBUG_INFO_DWARF5 -e GDB_SCRIPTS
./scripts/config -e FRAME_POINTER -d RANDOMIZE_BASE
make -j$(nproc)

# 2. QEMU 실행 (별도 터미널)
qemu-system-x86_64 \
    -kernel arch/x86/boot/bzImage \
    -append "root=/dev/sda console=ttyS0 nokaslr" \
    -drive file=rootfs.img,format=raw \
    -nographic \
    -s -S   # GDB stub 활성화 + 시작 시 정지

# 3. GDB 연결 (에디터에서 또는 직접)
gdb vmlinux -ex "target remote :1234" -ex "lx-symbols"
lx-* 명령어: CONFIG_GDB_SCRIPTS=y로 빌드하면 GDB에서 lx-dmesg(커널 로그), lx-ps(프로세스 목록), lx-lsmod(모듈 목록), lx-symbols(모듈 심볼 로드) 등의 커널 전용 명령어를 사용할 수 있습니다. VS Code와 Neovim의 디버거에서도 GDB 콘솔에서 이 명령어를 실행할 수 있습니다.

참고자료

이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.