커널 개발 에디터 설정
Vim, VS Code, Emacs, Neovim을 커널 개발에 최적화하는 설정 가이드입니다. compile_commands.json 생성, clangd LSP 통합, 에디터별 디버깅(Debugging) 연동까지 실전 워크플로를 정리합니다.
핵심 요약
- compile_commands.json — 모든 에디터의 코드 인텔리전스 기반입니다. 커널 빌드 후 반드시 생성해야 합니다.
- clangd — LSP(Language Server Protocol) 서버로, 정의 이동, 자동완성, 오류 표시 등을 제공합니다.
- 커널 코딩 스타일(Coding Style) — 탭 너비 8, 탭 문자 사용, 80/100칸 가이드라인을 에디터에 반영합니다.
- 디버깅 통합 — VS Code와 Neovim은 QEMU+GDB 커널 디버깅을 에디터 내에서 수행할 수 있습니다.
- 에디터 선택 — 핵심은 에디터 자체가 아니라 clangd + compile_commands.json 조합입니다.
단계별 이해
- 커널 빌드
먼저 커널을 한 번 이상 빌드하여compile_commands.json을 생성합니다. - clangd 설치
배포판 패키지로 clangd를 설치하고.clangd설정 파일을 작성합니다. - 에디터 연동
선호 에디터에 LSP 클라이언트를 설정하여 clangd와 연결합니다. - 워크플로 최적화
빌드 태스크(Task), 디버깅 설정, 코드 탐색 단축키를 구성합니다.
개요
커널 소스는 약 3만 개의 C 파일, 2,800만 줄 이상의 코드로 구성됩니다. 이 거대한 코드베이스를 효율적으로 탐색하려면 에디터와 언어 서버(Language Server)의 연동이 필수입니다. 모든 에디터의 핵심 흐름은 동일합니다.
위 구조에서 가장 중요한 파일은 compile_commands.json입니다. 이 파일이 없으면 clangd가 커널 소스의 include 경로와 매크로 정의를 알 수 없어, 에디터의 코드 인텔리전스가 동작하지 않습니다.
커널 개발 워크플로
에디터 설정의 궁극적인 목표는 편집→빌드→테스트→디버그 사이클을 에디터 내에서 빠르게 반복하는 것입니다. 아래 다이어그램은 커널 개발의 전형적인 워크플로를 보여줍니다.
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.py | scripts/clang-tools/gen_compile_commands.py | 커널 5.x+ | 빌드 후 .cmd 파일에서 추출 |
| bear | bear -- make -j$(nproc) | bear 패키지 | 빌드 래핑, 모든 프로젝트 범용 |
| compiledb | compiledb make -j$(nproc) | pip install compiledb | Python 기반, 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=memory | PCH를 메모리에 유지 | 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와의 오랜 통합 역사가 강점입니다.
.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가 여전히 유용합니다. 아래 다이어그램은 인덱싱과 코드 탐색 흐름을 보여줍니다.
vim-gutentags: 태그 자동 관리
vim-gutentags 플러그인은 파일 저장 시 ctags/cscope 데이터베이스를 백그라운드에서 자동 갱신합니다. 수동으로 make tags나 make 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 빠른 참조
| 작업 | ctags | cscope |
|---|---|---|
| 정의로 이동 | Ctrl+] | Ctrl+\ g |
| 뒤로 돌아가기 | Ctrl+t | Ctrl+o |
| 심볼 검색 | — | Ctrl+\ s |
| 호출하는 함수 찾기 | — | Ctrl+\ c |
| 호출되는 함수 찾기 | — | Ctrl+\ d |
| 텍스트 검색 | — | Ctrl+\ t |
| 데이터베이스 생성 | make tags | make cscope |
Visual Studio Code
VS Code는 풍부한 확장 생태계와 직관적인 UI, 내장 디버거를 제공합니다. 특히 Remote-SSH를 통한 원격 개발과 QEMU+GDB 커널 디버깅 통합이 강력합니다.
필수 확장 설치
| 확장 | 기능 | 비고 |
|---|---|---|
| clangd | LSP 기반 인텔리센스, 코드 탐색, 리팩토링 | 핵심 확장, 최우선 설치 |
| C/C++ (Microsoft) | 디버깅(GDB/LLDB), IntelliSense | clangd와 IntelliSense 충돌 주의 |
| GitLens | Git 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
}
}
"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,
},
}
에디터 선택 가이드
에디터마다 장점이 다르므로 개발 환경과 워크플로에 따라 선택하면 됩니다. 아래 비교표는 커널 개발 관점에서의 평가입니다.
워크플로별 권장 에디터
| 워크플로 | 권장 에디터 | 이유 |
|---|---|---|
| SSH로 원격 서버에서만 작업 | Vim / Neovim | 추가 설치 최소, 터미널에서 즉시 사용 |
| GUI 디버거로 커널 디버깅 | VS Code | launch.json으로 QEMU+GDB 통합, 브레이크포인트 클릭 설정 |
| 원격 서버 + 로컬 GUI 편집 | VS Code (Remote-SSH) | 로컬 UI, 원격 clangd/빌드 — 가장 직관적 |
| 최대 수준의 커스터마이징 | Emacs | Elisp로 모든 동작 제어, Org-mode로 커널 분석 노트 |
| 현대적 Vim + LSP 네이티브 | Neovim | 내장 LSP, treesitter, Lua 설정, 플러그인 생태계 |
| 다양한 에디터를 병행 | clangd + .clangd | 에디터 무관하게 동일한 코드 인텔리전스 보장 |
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-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)
checkpatch.pl --strict를 통과해야 합니다. 사소한 스타일 위반으로 패치가 거부되는 일이 빈번합니다. Git 훅(hook)으로 자동화하는 것을 권장합니다:
git diff HEAD~1 | ./scripts/checkpatch.pl -
QEMU+GDB 디버깅 통합 워크플로
에디터 내에서 커널을 소스 레벨로 디버깅하려면 QEMU, GDB, 에디터의 DAP/디버거 인터페이스를 연결해야 합니다. 아래 다이어그램은 전체 연동 구조를 보여줍니다.
# 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"
CONFIG_GDB_SCRIPTS=y로 빌드하면 GDB에서 lx-dmesg(커널 로그), lx-ps(프로세스 목록), lx-lsmod(모듈 목록), lx-symbols(모듈 심볼 로드) 등의 커널 전용 명령어를 사용할 수 있습니다. VS Code와 Neovim의 디버거에서도 GDB 콘솔에서 이 명령어를 실행할 수 있습니다.
참고자료
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.