커널 소스 코드 읽기 가이드

Linux 커널 소스 트리 구조 이해, ctags/cscope/clangd를 활용한 효율적인 코드 탐색, git 명령으로 함수 추적하기, LXR/Elixir 같은 온라인 도구 활용까지 커널 소스 읽기의 모든 것을 설명합니다.

문서 구조 재정렬: 이 문서는 소스 읽기 전략과 도구 선택 기준 중심으로 유지합니다. 빌드 시스템 심화는 빌드 시스템, 디버깅 심화는 디버깅 문서를 우선 참고하세요.
전제 조건: 커널 아키텍처 문서를 먼저 읽으세요. 소스 읽기는 전체 구조 이해가 먼저이므로, 각 서브시스템이 어디에 위치하는지 파악하는 것이 핵심입니다.
일상 비유: 이 주제는 대형 도서관에서 자료 찾기와 비슷합니다. 분류 체계를 먼저 파악하고, 색인과 검색 도구를 활용하면 원하는 정보를 빠르게 찾을 수 있듯이, 커널 소스도 구조를 먼저 익히고 적절한 도구를 사용하면 효율적으로 읽을 수 있습니다.

핵심 요약

  • 소스 트리 구조 — arch/, drivers/, fs/, kernel/, mm/, net/ 등 주요 디렉토리의 역할을 파악합니다.
  • ctags/cscope — 함수 정의/호출, 심볼 검색에 유용한 전통적인 도구입니다.
  • clangd/ccls — LSP 기반 현대적 도구로, IDE처럼 정확한 정의 이동과 자동 완성을 제공합니다.
  • git grep — 커밋 히스토리와 함께 코드를 검색하여 맥락을 파악하는 데 유용합니다.
  • 온라인 도구 — LXR, Elixir, Bootlin은 웹 브라우저로 커널 소스를 탐색할 수 있는 편리한 도구입니다.

단계별 이해

  1. 소스 다운로드git clone으로 최신 커널 소스를 받거나 kernel.org에서 특정 버전을 다운로드합니다.

    최신 소스는 https://git.kernel.org/에서 받을 수 있습니다.

  2. 디렉토리 구조 파악 — 주요 디렉토리의 역할을 먼저 이해합니다.

    arch/는 아키텍처별 코드, drivers/는 디바이스 드라이버, fs/는 파일시스템 등입니다.

  3. 태그 생성ctags 또는 cscope로 태그 파일을 생성하여 빠른 탐색을 준비합니다.

    또는 clangd를 설정하여 LSP 기반 탐색을 활용합니다.

  4. 함수 추적 — 관심 있는 기능의 진입점을 찾아 호출 체인을 따라갑니다.

    예: sys_read()vfs_read()file_operations->read()

관련 표준: POSIX.1-2017 (C API), ISO C11 — 커널이 구현하는 표준 API 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

커널 소스 트리 구조 (Source Tree Structure)

Linux 커널 소스 트리는 수천만 줄의 코드로 이루어져 있지만, 명확한 디렉토리 구조로 조직되어 있습니다. 각 디렉토리는 특정 목적을 가지며, 이 구조를 이해하는 것이 효율적인 소스 읽기의 첫걸음입니다.

최상위 디렉토리 (Top-Level Directories)

커널 소스의 최상위 디렉토리는 다음과 같이 구성됩니다.

디렉토리 역할 주요 내용
arch/ 아키텍처 의존 코드 x86, arm64, riscv 등 CPU 아키텍처별 부팅, 메모리 관리, 인터럽트 처리
drivers/ 디바이스 드라이버 블록, 네트워크, 그래픽, USB 등 하드웨어 드라이버 (전체 코드의 60% 이상)
fs/ 파일시스템 ext4, btrfs, XFS, VFS 등 파일시스템 구현
include/ 헤더 파일 공용 헤더, API 정의, 아키텍처별 헤더 (include/linux/, include/uapi/)
kernel/ 핵심 커널 서브시스템 스케줄러, 프로세스 관리, 타이머, 동기화 프리미티브
mm/ 메모리 관리 페이지 할당자, slab, vmalloc, 페이지 캐시
net/ 네트워킹 스택 TCP/IP, 라우팅, netfilter, 소켓
scripts/ 빌드 스크립트 Kbuild 시스템, 코드 생성, 헤더 체크 스크립트
Documentation/ 문서 서브시스템별 문서, API 가이드, 디바이스 트리
tools/ 유저 공간 도구 perf, BPF 도구, selftests
lib/ 공용 라이브러리 문자열 함수, 자료구조(rbtree, hash), 압축, CRC
security/ 보안 프레임워크 SELinux, AppArmor, TOMOYO, 키 관리
crypto/ 암호화 API 해시, 암호, 압축 알고리즘
ipc/ 프로세스 간 통신 System V IPC (메시지 큐, 세마포어, 공유 메모리)
💡

빠른 통계 확인: 커널 소스의 규모를 확인하려면 cloc 도구를 사용하세요. cloc --by-file-by-lang . 명령으로 디렉토리별 코드 라인 수를 확인할 수 있습니다. 대부분의 커널 버전에서 drivers/가 전체 코드의 60% 이상을 차지합니다.

헤더 파일 구조 (Include Directory)

헤더 파일은 커널의 API 정의를 담고 있어 소스 읽기에서 매우 중요합니다.

ℹ️

uapi 분리: Linux 3.7부터 유저 공간 API 헤더가 include/uapi/arch/*/include/uapi/로 분리되었습니다. 이는 커널 내부 구조 변경이 유저 공간 ABI를 깨뜨리지 않도록 하기 위함입니다.

진입점 찾기 (Finding Entry Points)

커널 코드를 읽을 때 어디서부터 시작해야 할지 막막할 수 있습니다. 다음은 주요 기능별 진입점입니다.

기능 진입점 위치
부팅 start_kernel() init/main.c
시스템 콜 SYSCALL_DEFINEx() 각 서브시스템 (예: fs/read_write.c)
스케줄링 schedule() kernel/sched/core.c
페이지 할당 alloc_pages() mm/page_alloc.c
네트워크 송신 dev_queue_xmit() net/core/dev.c
파일 열기 do_sys_open() fs/open.c

소스 다운로드 및 버전 관리 (Download and Version Control)

Git으로 소스 받기 (Git Clone)

Linux 커널은 Git으로 관리되며, 공식 저장소는 kernel.org에 있습니다. 최신 개발 버전을 받으려면 다음 명령을 사용합니다.

# Linus Torvalds의 공식 트리 (mainline)
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

# 특정 서브시스템 트리 (예: networking)
git clone https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git

# Stable 트리
git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
⚠️

저장소 크기: 커널 Git 저장소는 전체 히스토리를 포함하여 수 GB에 달합니다. 최신 코드만 필요하다면 --depth 1 옵션으로 shallow clone을 사용하세요. git clone --depth 1 https://git.kernel.org/...

Git으로 코드 검색 (Git Log and Grep)

Git은 단순한 버전 관리 도구가 아니라 강력한 코드 검색 도구입니다.

# 특정 문자열이 포함된 커밋 찾기
git log --all --grep="fix memory leak"

# 특정 함수가 변경된 커밋 찾기
git log -S "alloc_pages" --source --all

# 특정 파일의 변경 이력
git log -p mm/page_alloc.c

# 누가 특정 줄을 작성했는지 확인 (blame)
git blame -L 100,150 kernel/sched/core.c

# 전체 소스에서 패턴 검색 (git grep)
git grep "EXPORT_SYMBOL" -- "*.c"

# 특정 커밋에서 검색
git grep "kmalloc" v6.6

# 함수 정의 검색 (함수명만)
git grep -n "^[a-z_]* \+\*\?alloc_pages\s*("
💡

git grep vs grep: git grep은 일반 grep보다 훨씬 빠릅니다. Git 인덱스를 활용하여 검색하며, .gitignore를 자동으로 존중하므로 빌드 산출물이나 임시 파일을 제외합니다.

브랜치와 태그 (Branches and Tags)

커널 소스를 읽을 때는 적절한 버전을 선택하는 것이 중요합니다.

# 사용 가능한 태그 목록 (안정 버전)
git tag -l "v6.*"

# 특정 버전으로 체크아웃
git checkout v6.6

# 최신 개발 버전
git checkout master

# 두 버전 간 차이 확인
git diff v6.5..v6.6 -- mm/

# 특정 서브시스템의 변경사항만 보기
git log v6.5..v6.6 -- drivers/net/

ctags 설정 및 사용 (ctags Setup and Usage)

ctags는 소스 코드의 심볼(함수, 변수, 매크로 등)을 인덱싱하여 빠르게 정의로 이동할 수 있게 해주는 전통적인 도구입니다. Vim과 Emacs 같은 편집기와 긴밀하게 통합됩니다.

ctags 설치 (Installation)

대부분의 리눅스 배포판은 두 가지 ctags 구현을 제공합니다.

# Ubuntu/Debian
sudo apt install universal-ctags

# Fedora/RHEL
sudo dnf install ctags

# Arch Linux
sudo pacman -S ctags

태그 파일 생성 (Tag File Generation)

커널 소스 트리는 자체적으로 태그 생성을 지원합니다.

# 커널 소스 최상위 디렉토리에서
make tags          # ctags 태그 생성 (vim용)
make cscope        # cscope 데이터베이스 생성
make TAGS          # etags 생성 (emacs용)

# 특정 아키텍처만 포함 (더 빠르고 정확)
make ARCH=x86_64 tags

# 수동으로 ctags 생성 (더 세밀한 제어)
ctags -R --exclude=.git --exclude=*.o --exclude=*.a \
      --languages=C,C++ --langmap=c:+.h .
ℹ️

태그 파일 크기: 전체 커널 소스의 태그 파일은 100MB 이상 될 수 있습니다. ARCH= 옵션으로 특정 아키텍처만 포함하면 크기와 생성 시간을 크게 줄일 수 있습니다.

Vim에서 ctags 사용 (ctags in Vim)

Vim은 ctags를 네이티브로 지원합니다.

" ~/.vimrc에 추가
set tags=./tags,tags;$HOME  " 현재 디렉토리부터 홈까지 tags 파일 검색

Vim 내에서 다음 명령을 사용합니다.

명령 설명
Ctrl-] 커서 위치의 심볼 정의로 이동
Ctrl-T 이전 위치로 돌아가기 (태그 스택 pop)
:tag function_name 특정 태그로 이동
:ts 또는 :tselect 태그 목록 표시 (같은 이름의 여러 정의가 있을 때)
:tn, :tp 다음/이전 태그로 이동
g] 태그 목록 표시 (선택 가능)

Emacs에서 ctags 사용 (ctags in Emacs)

Emacs는 etags 형식을 사용합니다.

# TAGS 파일 생성
make TAGS

# Emacs 내에서 태그 파일 로드
M-x visit-tags-table RET /path/to/linux/TAGS RET

Emacs 내에서 다음 명령을 사용합니다.

cscope 설정 및 사용 (cscope Setup and Usage)

cscope는 C 코드 탐색에 특화된 도구로, ctags보다 더 강력한 검색 기능을 제공합니다. 함수 호출자 찾기, 전역 변수 참조 찾기 등 양방향 탐색이 가능합니다.

cscope 설치 (Installation)

# Ubuntu/Debian
sudo apt install cscope

# Fedora/RHEL
sudo dnf install cscope

# Arch Linux
sudo pacman -S cscope

데이터베이스 생성 (Database Generation)

# 커널 Makefile 사용 (추천)
make cscope

# 수동 생성
find . -name "*.c" -o -name "*.h" > cscope.files
cscope -b -q -k

# -b: GUI 없이 데이터베이스만 생성
# -q: 빠른 검색을 위한 역인덱스 생성
# -k: /usr/include 제외 (커널 모드)

cscope 사용법 (Usage)

cscope는 TUI(Terminal UI)와 Vim/Emacs 통합 두 가지 방식으로 사용할 수 있습니다.

# TUI 모드로 실행
cscope -d    # -d: 데이터베이스 재생성 안 함

TUI 모드에서 사용 가능한 검색 유형은 다음과 같습니다.

Vim에서 cscope 사용 (cscope in Vim)

" ~/.vimrc에 추가
if has("cscope")
  set csprg=/usr/bin/cscope
  set csto=0
  set cst
  set nocsverb
  if filereadable("cscope.out")
    cs add cscope.out
  endif
  set csverb
endif

" 키 매핑 (선택 사항)
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-\>d :cs find d <C-R>=expand("<cword>")<CR><CR>

Vim 내에서 다음 명령을 사용합니다.

명령 설명
:cs find s symbol 심볼의 모든 참조 찾기
:cs find g symbol 전역 정의 찾기
:cs find c function 이 함수를 호출하는 모든 함수 찾기
:cs find d function 이 함수가 호출하는 모든 함수 찾기
:cs find t text 문자열 검색
:cs find e pattern egrep 패턴 검색
:cs find f file 파일 찾기
:cs find i file 이 파일을 include하는 파일 찾기
💡

cscope의 강점: cscope는 "누가 이 함수를 호출하는가?"를 찾는 데 매우 유용합니다. ctags는 정의로 이동하는 것만 지원하지만, cscope는 호출 관계를 양방향으로 추적할 수 있습니다.

clangd / ccls (LSP 기반 도구)

Language Server Protocol(LSP)는 현대적인 IDE 기능을 모든 편집기에서 사용할 수 있게 해주는 표준입니다. clangd와 ccls는 C/C++ LSP 서버로, ctags/cscope보다 훨씬 정확한 코드 분석을 제공합니다.

LSP 도구의 장점 (Advantages)

clangd 설정 (clangd Setup)

clangd를 사용하려면 먼저 compile_commands.json 파일이 필요합니다. 이 파일은 각 소스 파일을 어떻게 컴파일하는지에 대한 정보를 담고 있습니다.

# clangd 설치
sudo apt install clangd-15    # Ubuntu/Debian
sudo dnf install clang-tools-extra    # Fedora/RHEL

# 커널 소스에서 compile_commands.json 생성
# 방법 1: 커널 스크립트 사용 (Linux 5.10+)
make defconfig
scripts/clang-tools/gen_compile_commands.py

# 방법 2: bear 사용 (모든 버전)
sudo apt install bear
bear -- make -j$(nproc)

# compile_commands.json이 생성되면 clangd가 자동으로 인식
⚠️

아키텍처 설정: compile_commands.json은 특정 아키텍처에 대해 생성됩니다. ARCH=arm64로 빌드했다면, clangd도 ARM64 헤더를 사용합니다. x86_64와 ARM64를 동시에 읽어야 한다면 여러 개의 compile_commands.json을 전환해야 합니다.

편집기 통합 (Editor Integration)

대부분의 현대 편집기는 LSP를 지원합니다.

Visual Studio Code

# clangd 확장 설치
# Extensions → "clangd" 검색 → 설치

# settings.json에 추가 (선택 사항)
{
  "clangd.arguments": [
    "--background-index",
    "--clang-tidy",
    "--header-insertion=never",
    "--compile-commands-dir=${workspaceFolder}"
  ]
}

Vim/Neovim

" vim-plug 사용 예시
Plug 'neoclide/coc.nvim', {'branch': 'release'}

" coc-settings.json에 추가
{
  "languageserver": {
    "clangd": {
      "command": "clangd",
      "args": ["--background-index"],
      "rootPatterns": ["compile_commands.json", ".git/"],
      "filetypes": ["c", "cpp", "h"]
    }
  }
}

Emacs

;; lsp-mode 사용
(use-package lsp-mode
  :hook ((c-mode . lsp)
         (c++-mode . lsp))
  :commands lsp)

(use-package lsp-ui :commands lsp-ui-mode)

ccls (대안)

ccls는 clangd의 대안으로, 일부 사용자는 더 빠르다고 평가합니다.

# ccls 설치
sudo apt install ccls    # Ubuntu 22.04+

# .ccls 파일 생성 (프로젝트 루트에)
cat > .ccls <<EOF
clang
%compile_commands.json
EOF

온라인 도구 (Online Tools)

로컬 도구 설정이 번거롭거나, 빠르게 코드를 확인하고 싶을 때 온라인 도구가 유용합니다.

Elixir (Bootlin)

URL: https://elixir.bootlin.com/

Bootlin이 운영하는 Elixir는 가장 널리 사용되는 온라인 커널 소스 브라우저입니다.

LXR (Linux Cross-Referencer)

LXR은 초기의 온라인 소스 브라우저로, 현재는 대부분 Elixir로 대체되었지만 일부 사이트에서 여전히 운영됩니다.

GitHub / GitLab

커널 공식 미러가 GitHub에 있습니다.

커널 공식 문서

URL: https://www.kernel.org/doc/html/latest/

소스 코드 내 Documentation/ 디렉토리를 Sphinx로 빌드한 공식 문서입니다.

💡

로컬 문서 빌드: 커널 소스 트리에서 make htmldocs를 실행하면 Documentation/output/html/에 HTML 문서가 생성됩니다. 오프라인 환경에서 유용합니다.

함수 호출 추적 방법 (Tracing Function Calls)

커널 코드를 읽을 때 가장 중요한 작업 중 하나는 함수 호출 체인을 추적하는 것입니다. "이 함수는 누가 호출하는가?", "이 함수는 어떤 함수를 호출하는가?"를 파악하는 것이 코드 이해의 핵심입니다.

순방향 추적 (Forward Tracing)

함수가 호출하는 함수를 찾는 것은 비교적 쉽습니다.

# grep으로 함수 호출 찾기
git grep "kmalloc\s*(" -- "*.c"

# cscope로 찾기
cscope -d
# → "Find functions called by this function" 선택

# clangd 사용 (VS Code / Vim)
# 함수 내부에서 "Go to Definition" (F12)

역방향 추적 (Backward Tracing)

함수를 호출하는 함수를 찾는 것은 더 중요하지만 어렵습니다.

# cscope가 가장 효과적
cscope -d
# → "Find functions calling this function" 선택

# git grep으로 찾기 (정규식 사용)
git grep -n "vfs_read\s*(" -- "*.c"

# clangd 사용 (VS Code / Vim)
# 함수 정의에서 "Find All References" (Shift+F12)

간접 호출 추적 (Indirect Calls)

커널은 함수 포인터를 광범위하게 사용합니다. 예를 들어 file_operations 구조체는 파일 시스템별로 다른 함수를 가리킵니다.

# 구조체 정의 찾기
git grep "struct file_operations.*=" -- fs/ext4/

# 예: ext4의 file_operations
# fs/ext4/file.c:
# const struct file_operations ext4_file_operations = {
#     .read_iter = ext4_file_read_iter,
#     .write_iter = ext4_file_write_iter,
#     ...
# };

# 이제 ext4_file_read_iter가 실제 구현임을 알 수 있음
💡

함수 포인터 추적 팁: 커널의 많은 서브시스템은 _operations 구조체로 함수 포인터를 관리합니다. file_operations, super_operations, inode_operations, net_device_ops 등을 찾으면 실제 구현을 추적할 수 있습니다.

Kconfig와 Makefile 읽는 법 (Understanding Kconfig and Makefile)

커널 소스를 읽을 때 C 코드만 보면 안 됩니다. Kconfig와 Makefile을 읽어야 코드가 언제 포함되는지, 어떤 조건에서 빌드되는지 이해할 수 있습니다.

Kconfig 기본 (Kconfig Basics)

Kconfig 파일은 커널 설정 옵션을 정의합니다.

# fs/ext4/Kconfig 예시
config EXT4_FS
    tristate "The Extended 4 (ext4) filesystem"
    select JBD2
    select CRC16
    select CRYPTO
    help
      This is the next generation of the ext3 filesystem.

config EXT4_USE_FOR_EXT2
    bool "Use ext4 for ext2/ext3 file systems"
    depends on EXT4_FS
    depends on EXT2_FS=n && EXT3_FS=n

주요 키워드는 다음과 같습니다.

ℹ️

Kconfig 파일 위치: 각 디렉토리에 Kconfig 파일이 있으며, 최상위 Kconfigsource 지시어로 하위 Kconfig를 포함합니다. 예: source "fs/Kconfig"

Makefile 기본 (Makefile Basics)

각 디렉토리의 Makefile은 빌드 대상을 정의합니다.

# fs/ext4/Makefile 예시
obj-$(CONFIG_EXT4_FS) += ext4.o

ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o \
          extents.o extents_status.o file.o fsmap.o fsync.o \
          hash.o ialloc.o indirect.o inline.o inode.o ioctl.o \
          mballoc.o migrate.o mmp.o move_extent.o namei.o page-io.o \
          readpage.o resize.o super.o symlink.o sysfs.o xattr.o \
          xattr_trusted.o xattr_user.o

ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o

주요 패턴은 다음과 같습니다.

설정으로 코드 찾기 (Finding Code by Config)

# 특정 CONFIG 심볼이 정의된 Kconfig 찾기
git grep "config EXT4_FS" -- "*Kconfig*"

# 특정 CONFIG 심볼을 사용하는 Makefile 찾기
git grep "CONFIG_EXT4_FS" -- "*Makefile*"

# 소스 코드에서 CONFIG 심볼 사용 찾기
git grep "CONFIG_EXT4_FS" -- "*.c" "*.h"

# 현재 설정 확인
grep EXT4_FS .config

Documentation/ 디렉토리 활용 (Using Documentation/)

커널 소스의 Documentation/ 디렉토리는 보물 창고입니다. 코드를 읽기 전에 관련 문서를 먼저 읽으면 시간을 크게 절약할 수 있습니다.

문서 구조 (Documentation Structure)

디렉토리 내용
admin-guide/ 시스템 관리자를 위한 가이드 (sysctl, 커널 파라미터 등)
driver-api/ 드라이버 개발 API 문서
process/ 커널 개발 프로세스, 패치 제출 가이드
core-api/ 핵심 커널 API (메모리 관리, 동기화 등)
filesystems/ 파일시스템별 문서
networking/ 네트워킹 스택 문서
devicetree/ 디바이스 트리 바인딩
translations/ 번역 문서 (한국어 포함)

주요 문서 (Key Documents)

소스 읽기를 시작하기 전에 다음 문서를 추천합니다.

문서 빌드 (Building Documentation)

# HTML 문서 생성
make htmldocs

# PDF 생성 (LaTeX 필요)
make pdfdocs

# 생성된 문서 위치
ls Documentation/output/html/index.html

# 웹 서버로 문서 보기
python3 -m http.server 8080 --directory Documentation/output/html/
⚠️

빌드 의존성: 문서 빌드에는 Sphinx, graphviz 등이 필요합니다. Ubuntu/Debian: sudo apt install python3-sphinx graphviz

실전 예제: sys_read 구현 찾기 (Practical Example: Finding sys_read)

실제로 커널 소스를 읽는 워크플로우를 시연하기 위해, read() 시스템 콜의 구현을 찾아가는 과정을 단계별로 설명합니다.

1단계: 시스템 콜 진입점 찾기

시스템 콜은 SYSCALL_DEFINE 매크로로 정의됩니다.

# read 시스템 콜 정의 검색
git grep "SYSCALL_DEFINE.*read" -- "*.c"

# 결과 예시:
# fs/read_write.c:SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)

2단계: 구현 읽기

fs/read_write.c를 열어 SYSCALL_DEFINE3(read, ...)를 찾습니다.

// fs/read_write.c (simplified)
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
    struct fd f = fdget_pos(fd);
    loff_t pos;

    if (!f.file)
        return -EBADF;

    pos = file_pos_read(f.file);
    ssize_t ret = vfs_read(f.file, buf, count, &pos);
    if (ret >= 0)
        file_pos_write(f.file, pos);
    fdput_pos(f);
    return ret;
}

3단계: vfs_read 추적

vfs_read() 함수로 이동합니다. ctags/cscope/clangd 중 하나를 사용합니다.

# git grep으로 정의 찾기
git grep -n "^ssize_t vfs_read" -- "*.c"

# 결과: fs/read_write.c:xxx:ssize_t vfs_read(...)
// fs/read_write.c
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
    // 권한 검사
    if (!(file->f_mode & FMODE_READ))
        return -EBADF;

    // file_operations의 read 또는 read_iter 호출
    if (file->f_op->read_iter)
        return new_sync_read(file, buf, count, pos);
    else if (file->f_op->read)
        return file->f_op->read(file, buf, count, pos);
    else
        return -EINVAL;
}

4단계: 실제 구현 찾기

이제 파일시스템별 구현을 찾아야 합니다. 예를 들어 ext4의 경우:

# ext4의 file_operations 찾기
git grep "struct file_operations.*=" -- fs/ext4/

# fs/ext4/file.c:
# const struct file_operations ext4_file_operations = {
#     .read_iter = ext4_file_read_iter,
#     ...
# };

이제 ext4_file_read_iter()를 찾아 읽으면 됩니다.

5단계: 호출 체인 시각화

지금까지 추적한 호출 체인은 다음과 같습니다.

read() (유저 공간)
  ↓
sys_read() (fs/read_write.c)
  ↓
vfs_read() (fs/read_write.c)
  ↓
file->f_op->read_iter (함수 포인터)
  ↓
ext4_file_read_iter() (fs/ext4/file.c)
  ↓
generic_file_read_iter() (mm/filemap.c)
  ↓
page cache 또는 disk I/O
💡

호출 체인 추적 팁: 시스템 콜 → VFS 계층 → 파일시스템별 구현 → 블록 계층 순으로 내려가는 패턴이 일반적입니다. sys_*vfs_*do_* → 파일시스템 구조체 함수 포인터 순입니다.

실용적인 명령어 모음 (Command Cheatsheet)

자주 사용하는 소스 탐색 명령을 빠르게 참고할 수 있도록 정리했습니다.

Git 명령어 (Git Commands)

# 함수 정의 검색
git grep -n "^int alloc_pages" -- "*.c"

# 구조체 정의 검색
git grep -n "^struct task_struct" -- "*.h"

# 매크로 정의 검색
git grep -n "#define CONFIG_" -- "*.h"

# 특정 파일 타입만 검색
git grep "kmalloc" -- "*.c"

# 대소문자 무시 검색
git grep -i "spinlock" -- "*.h"

# 단어 단위 검색 (-w)
git grep -w "init" -- "*.c"

# 줄 번호와 함수명 표시
git grep -n -p "printk" -- kernel/

# 특정 심볼이 추가/삭제된 커밋 찾기
git log -S "rcu_read_lock" --oneline

# 특정 함수를 수정한 커밋들
git log -L :schedule:kernel/sched/core.c

파일 찾기 (Finding Files)

# 파일명으로 찾기
find . -name "sched.h"

# 최근 수정된 파일 찾기
find . -name "*.c" -mtime -7

# 특정 크기 이상 파일
find . -name "*.c" -size +100k

# git ls-files 사용 (더 빠름)
git ls-files | grep sched

유용한 grep 패턴 (Useful Grep Patterns)

# 함수 정의 (반환 타입 + 함수명)
git grep -n "^\w\+\s\+\*\?\w\+\s*("

# EXPORT_SYMBOL로 export된 함수
git grep "EXPORT_SYMBOL" -- "*.c"

# 시스템 콜 정의
git grep "SYSCALL_DEFINE"

# 커널 파라미터
git grep "module_param"

# 디바이스 트리 바인딩
git grep "compatible.*=" -- "*.dts"

팁과 주의사항 (Tips and Warnings)

효율적인 읽기 팁 (Reading Tips)

💡

Top-down vs Bottom-up: 큰 그림을 먼저 파악하고 싶다면 Top-down(시스템 콜 → 하위 계층)으로, 특정 알고리즘을 이해하고 싶다면 Bottom-up(핵심 함수 → 호출자)으로 읽으세요.

💡

문서 우선: 코드를 읽기 전에 Documentation/을 먼저 읽으세요. 많은 서브시스템이 상세한 설계 문서를 제공합니다.

💡

커밋 메시지 읽기: git loggit blame으로 코드가 왜 그렇게 작성되었는지 맥락을 파악하세요. 커밋 메시지에 설계 결정 이유가 설명되어 있습니다.

💡

버전 고정: 소스를 읽을 때는 특정 stable 버전으로 체크아웃하세요. master 브랜치는 계속 변경되므로 일관성이 떨어집니다.

흔한 실수 (Common Pitfalls)

⚠️

매크로 확장 무시: 커널은 매크로를 광범위하게 사용합니다. SYSCALL_DEFINE, module_init 같은 매크로의 실제 확장을 이해하지 않으면 코드 흐름을 놓칠 수 있습니다. gcc -E로 전처리 결과를 확인하세요.

⚠️

조건부 컴파일 간과: #ifdef CONFIG_XXX로 둘러싸인 코드는 설정에 따라 포함되지 않을 수 있습니다. .config 파일을 확인하세요.

⚠️

아키텍처 차이 무시: arch/ 아래 코드는 아키텍처마다 다릅니다. x86과 ARM의 메모리 관리 코드가 다르므로, 읽는 아키텍처를 명확히 하세요.

성능 고려사항 (Performance Notes)

ℹ️

태그 재생성: 소스가 변경되면 태그 파일도 재생성해야 합니다. make tags는 빠르지만, 수동 재생성 스크립트를 만들어두면 편리합니다.

ℹ️

clangd 인덱싱 시간: clangd는 첫 실행 시 전체 코드를 인덱싱하므로 시간이 걸립니다. 백그라운드에서 완료될 때까지 기다리세요.

워크플로우 예시 (Workflow Example)

실제 커널 개발자의 일반적인 소스 읽기 워크플로우를 정리했습니다.

시나리오: 버그 수정을 위한 소스 읽기

  1. 증상 파악 — 버그 리포트나 로그를 읽고 문제 영역을 특정합니다.
  2. 진입점 찾기 — 시스템 콜, ioctl, 또는 커널 API로 진입점을 찾습니다.
  3. 호출 체인 추적 — cscope나 clangd로 호출 관계를 따라갑니다.
  4. 관련 코드 읽기 — 의심되는 함수와 그 주변 코드를 읽습니다.
  5. git blame으로 히스토리 확인 — 최근 변경사항이 버그를 유발했는지 확인합니다.
  6. 문서 참고Documentation/에서 설계 의도를 확인합니다.
  7. 테스트 코드 확인tools/testing/selftests/에서 관련 테스트를 찾아 실행합니다.

시나리오: 새 기능 구현을 위한 소스 읽기

  1. 유사 기능 찾기 — 이미 구현된 비슷한 기능을 검색합니다.
  2. API 문서 읽기Documentation/core-api/에서 사용할 커널 API를 파악합니다.
  3. 예제 드라이버 분석drivers/에서 레퍼런스 드라이버를 찾아 분석합니다.
  4. Kconfig/Makefile 패턴 파악 — 빌드 시스템 통합 방법을 배웁니다.
  5. 코딩 스타일 확인process/coding-style.rst를 읽고 따릅니다.

실무 소스 리딩 플레이북

실제 커널 분석에서는 "어디서부터 읽을지"보다 "어떤 순서로 좁혀갈지"가 더 중요합니다. 아래 루틴은 버그 분석과 기능 학습 모두에서 재사용 가능한 표준 절차입니다.

단계 핵심 질문 실행 명령/도구
범위 고정 대상 커널 버전과 아키텍처가 무엇인가? git checkout vX.Y, uname -m
진입점 확보 첫 호출 지점(syscall/ioctl/interrupt)은 어디인가? git grep, ctags, cscope
호출 체인 확장 직접 호출/간접 호출 경로는 무엇인가? clangd, git grep "ops->"
조건 확인 CONFIG, 아키텍처 조건으로 경로가 바뀌는가? git grep "CONFIG_", .config
역사 추적 왜 이렇게 구현되었는가? git blame, git log -L

리딩 세션 템플릿

# 1) 대상 버전 고정
git checkout v6.12

# 2) 관심 심볼 최초 검색
git grep -n "SYSCALL_DEFINE.*openat" -- "*.c"

# 3) 호출 관계 추적
git grep -n "do_sys_openat2" -- "*.c" "*.h"

# 4) 설정 조건 확인
git grep -n "CONFIG_.*OPEN" -- "*Kconfig*" "*Makefile*" "*.c"

# 5) 변경 이력 확인
git log -L :do_sys_openat2:fs/open.c
⚠️

빈번한 실패 패턴: 최신 master 기준으로만 읽다가 문서/배포 커널 버전과 코드가 달라 혼동하는 경우가 많습니다. 분석 대상 버전을 먼저 고정한 뒤 탐색하세요.

커널 소스 읽기와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.