- Published on
커널 공부는 어디서 시작하는가 — 4,300만 줄 앞에서 길을 잃지 않는 법
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며 — 이 시리즈가 앞선 글들과 다른 점
- 1. 커널은 어떤 코드베이스인가 — 숫자부터
- 2. 트리 최상위 지도
- 3. "처음부터 순서대로 읽기"가 실패하는 이유
- 4. 현실적인 진입 경로 세 갈래
- 5. 정말로 필요한 전제 조건
- 6. 필요하지 않은 것
- 7. 여덟 편을 어떤 순서로 볼 것인가
- 흔한 함정
- 마치며
- 직접 해보기
- 다음 편
- 참고 자료
들어가며 — 이 시리즈가 앞선 글들과 다른 점
이 블로그에는 커널 이야기가 이미 두 층으로 쌓여 있습니다.
하나는 운영체제 개념 시리즈입니다. 프로세스가 무엇이고 스케줄링이 무엇이며 가상 메모리가 어떤 문제를 푸는지를 교과서 층위에서 설명합니다. 다른 하나는 운영 층위의 글들입니다. 리눅스 CPU 스케줄러의 진화는 CFS와 EEVDF가 어떻게 동작하는지를, 패킷의 여정은 패킷이 커널 안에서 어떤 경로를 지나는지를, 컨테이너는 거짓말이다는 네임스페이스와 cgroup이 실제로 무엇인지를 다룹니다.
이 시리즈는 그 두 층 어디에도 속하지 않습니다. 앞의 글들은 커널이 무엇을 하는지 설명합니다. 이 시리즈는 그 동작을 구현한 코드를 직접 열어 읽고, 빌드하고, 고쳐 보는 법을 다룹니다. 스케줄러가 무엇인지 다시 설명하지 않는 대신, kernel/sched/ 아래에서 그 스케줄러를 구현한 파일을 찾아내는 방법을 다룹니다.
여덟 편을 다 읽고 나면 독자는 이런 것을 할 수 있게 됩니다. 어떤 동작이 이상하다고 느꼈을 때 그 동작을 구현한 함수를 찾아내고, 커널을 빌드해 가상 머신에서 부팅하고, 실행 중인 커널이 그 함수를 지나가는 것을 눈으로 확인하고, 작은 모듈을 하나 작성하고, 첫 패치를 메일링 리스트에 보내는 것입니다. 스케줄러의 정의를 암송하는 능력은 여기 포함되지 않습니다.
첫 편은 지도입니다. 코드를 열기 전에 이 코드베이스가 어떤 물건인지부터 봅니다.
1. 커널은 어떤 코드베이스인가 — 숫자부터
추상적인 "엄청나게 크다"는 말은 도움이 되지 않습니다. 그래서 실제로 받아서 세어 봤습니다.
2026년 8월 19일 기준으로 메인라인 트리를 얕게 복제하고, git이 추적하는 모든 파일의 줄 수를 최상위 디렉터리별로 합산한 결과입니다. 이때 트리의 최상위 Makefile은 버전을 7.2로 표시하고 있었고, HEAD는 7.3 병합 창구가 열린 직후의 커밋이었습니다.
| 디렉터리 | 추적 파일 줄 수 | 전체 대비 | 무엇이 들어 있는가 |
|---|---|---|---|
drivers | 26,319,110 | 60.9% | 장치 드라이버 전부 |
arch | 4,794,130 | 11.1% | CPU 아키텍처별 코드 |
tools | 2,447,077 | 5.7% | perf, bpftool 등 사용자 공간 도구 |
sound | 1,688,811 | 3.9% | ALSA 사운드 서브시스템 |
Documentation | 1,672,908 | 3.9% | 공식 문서 트리 |
fs | 1,647,593 | 3.8% | VFS와 개별 파일시스템 |
include | 1,461,808 | 3.4% | 헤더 |
net | 1,307,979 | 3.0% | 네트워크 스택 |
kernel | 564,489 | 1.3% | 스케줄러, 시그널, 시간, 모듈 로더 |
lib | 350,616 | 0.8% | 자료구조와 유틸리티 |
mm | 225,613 | 0.5% | 메모리 관리 |
rust | 162,716 | 0.4% | 러스트 추상화 계층 |
security | 129,248 | 0.3% | LSM과 보안 모듈 |
scripts | 125,483 | 0.3% | 빌드와 검사 도구 |
| 전체 | 43,223,394 | 100% | 추적 파일 94,870개 |
이 표에서 읽어야 할 것은 딱 하나입니다. 드라이버가 전체의 60퍼센트를 넘습니다. 그리고 드라이버는 서로 거의 독립적입니다. 어떤 무선랜 칩의 펌웨어 로딩 코드는 다른 어떤 코드도 참조하지 않고, 당신이 그 칩을 쓰지 않는다면 평생 열어 볼 일이 없습니다. 아키텍처 디렉터리도 마찬가지입니다. x86_64에서 공부한다면 arch/ 아래 480만 줄 중 대부분은 다른 CPU의 이야기입니다.
이렇게 빼고 나면 무엇이 남는지 세어 보는 편이 훨씬 유용합니다. 스케줄러와 시그널과 모듈 로더가 있는 kernel, 메모리 관리의 mm, 그리고 파일시스템 계층의 핵심부를 합치면 100만 줄 안쪽입니다. 여기에 헤더를 더해도 커널 전체의 5퍼센트가 되지 않습니다. 커널 내부를 이해한다고 말할 때 사람들이 실제로 읽는 영역이 대략 이 정도입니다.
숫자를 하나 더 붙이자면, 이 표에서 러스트는 16만 줄로 전체의 0.4퍼센트입니다. 커널이 러스트로 옮겨 가고 있다는 이야기를 들었다면, 지금 시점의 규모는 이 정도라는 사실을 알고 시작하는 편이 좋습니다.
2. 트리 최상위 지도
같은 시점에 확인한 최상위 디렉터리 전체 목록입니다. 스물세 개뿐이고, 이름만 봐도 대부분 짐작이 갑니다.
Documentation LICENSES arch block certs crypto drivers fs
include init io_uring ipc kernel lib mm net rust samples
scripts security sound tools usr virt
파일로는 Kbuild, Kconfig, MAINTAINERS, Makefile, README, COPYING, CREDITS가 최상위에 있고, .clang-format과 .get_maintainer.ignore 같은 점 파일도 함께 있습니다.
이 중 처음에 알아 두면 좋은 것만 골라 설명하면 이렇습니다.
init— 부팅 이후 커널이 스스로를 초기화하는 코드가 있습니다. 7,150줄뿐이라 통째로 읽어 볼 만한 몇 안 되는 디렉터리입니다.kernel— 스케줄러, 시그널, 시간, cgroup, 모듈 로더, 추적 인프라가 전부 여기 있습니다. "커널의 커널"에 해당합니다.mm— 페이지 할당기, 슬랩 할당기, 페이지 폴트 처리, 스왑이 여기 있습니다. 22만 줄로 작지만 밀도가 가장 높은 곳 중 하나입니다.fs— VFS 계층과 개별 파일시스템이 섞여 있습니다. VFS만 보고 싶다면 최상위의.c파일들부터 보면 됩니다.include/linux— 커널 내부 API의 선언이 모여 있습니다. 함수 하나가 무엇을 하는지 궁금할 때 가장 먼저 여는 곳입니다.include/uapi— 사용자 공간과의 계약입니다. 여기 있는 것은 함부로 바꿀 수 없습니다.samples— 커널이 직접 관리하는 예제 코드입니다. 모듈, kprobes, ftrace, kobject 예제가 들어 있고, 4편과 5편에서 다시 씁니다.scripts—checkpatch.pl,get_maintainer.pl,decode_stacktrace.sh,faddr2line처럼 실제로 쓰게 될 도구가 여기 있습니다.tools— perf와 bpftool 같은 사용자 공간 도구입니다. 커널이 아니라 커널을 다루는 프로그램입니다.
3. "처음부터 순서대로 읽기"가 실패하는 이유
커널 공부를 시작하는 사람이 가장 자주 세우는 계획은 이런 모양입니다. 좋은 책을 한 권 사서, 1장부터 순서대로 읽고, 대응하는 소스를 옆에 띄워 놓고 따라간다. 제가 관찰한 범위에서 이 계획은 거의 항상 3장쯤에서 멈춥니다. 이유는 게으름이 아니라 구조입니다.
첫째, 커널에는 따라갈 실행 흐름이 하나가 아닙니다. 응용 프로그램은 main에서 시작해 아래로 흘러가므로 위에서부터 읽는 것이 자연스럽습니다. 커널에도 시작점은 있습니다. init/main.c의 start_kernel이 그것입니다. 그런데 그 함수가 끝나고 나면 커널은 아무것도 "실행"하지 않고 가만히 있습니다. 그 뒤로 커널 코드가 실행되는 경우는 전부 누군가가 밖에서 두드릴 때입니다. 시스템 콜이 들어오거나, 인터럽트가 걸리거나, 타이머가 만료되거나, 워크큐가 깨어날 때입니다. 즉 커널은 수백 개의 진입점을 가진 라이브러리에 가깝고, 이런 코드는 위에서 아래로 읽는 대상이 아닙니다.
둘째, 코드가 계속 움직입니다. 구체적인 예를 하나 들겠습니다. x86_64에서 시스템 콜이 커널로 들어온 뒤 처음 도착하는 C 함수는 do_syscall_64입니다. 이 함수는 오랫동안 arch/x86/entry/common.c에 있었고, 지금도 그렇게 설명하는 자료가 인터넷에 아주 많습니다. 그런데 제가 확인한 트리에서 이 함수는 arch/x86/entry/syscall_64.c의 87번째 줄에 있었습니다. 자료가 틀린 것이 아니라, 그 자료가 쓰인 시점 이후에 파일이 갈라진 것입니다. 순서대로 읽는 계획은 이런 어긋남을 만날 때마다 멈춥니다. 반면 "이 함수를 찾겠다"는 계획은 어긋남을 만나도 멈추지 않습니다. 찾으면 되기 때문입니다.
셋째, 순서가 없기 때문에 진도를 잴 수 없습니다. 400페이지 중 120페이지를 읽었다는 것은 측정 가능하지만, 4,300만 줄 중 얼마를 읽었다는 것은 아무 의미가 없습니다. 측정할 수 없는 목표는 유지되지 않습니다.
그래서 이 시리즈가 권하는 방식은 정반대입니다. 질문을 하나 손에 들고 트리에 들어갑니다. "read를 호출하면 커널 안에서 어떤 함수들을 지나는가" 같은 질문이면 충분합니다. 질문이 있으면 읽을 파일이 열 개 안쪽으로 줄어들고, 답을 얻었는지 아닌지도 스스로 판단할 수 있습니다. 2편이 그 찾는 기술을 다루고, 4편이 그 질문 하나를 끝까지 따라가는 실습입니다.
4. 현실적인 진입 경로 세 갈래
커널에 들어가는 문은 하나가 아닙니다. 크게 세 갈래로 나뉘고, 어느 쪽이 맞는지는 지금 무엇을 하고 있는 사람인지에 달려 있습니다.
읽기 — 소스에서 출발하기
트리를 받아 코드를 직접 읽는 방식입니다. 이미 구체적인 질문이 있는 사람에게 가장 빠릅니다. 예를 들어 "왜 이 시스템 콜이 EINVAL을 돌려주는가"처럼 답이 코드 안에 확실히 있는 질문이라면, 관측 도구를 배우는 것보다 해당 함수를 찾아 읽는 편이 몇 배 빠릅니다.
대신 질문 없이 시작하면 3절에서 말한 실패로 곧장 갑니다. 이 경로의 전제 조건은 C가 아니라 질문입니다.
관측 — 실행 중인 커널에서 출발하기
ftrace, perf, kprobes, eBPF로 지금 돌아가는 커널이 무슨 함수를 지나는지 관찰하는 방식입니다. 시스템을 운영하는 사람에게 가장 자연스럽습니다. 이미 손에 있는 서버가 실험 대상이고, 배운 것이 곧바로 업무에 쓰이기 때문입니다.
이 블로그의 eBPF 관측성 실전이나 로드 애버리지 해설 같은 글이 이 경로의 입구에 해당합니다. 관측 경로의 강점은 커널을 빌드하지 않아도 시작할 수 있다는 것이고, 약점은 함수 이름까지는 보이지만 그 함수가 왜 그렇게 쓰였는지는 보이지 않는다는 것입니다. 결국 소스로 돌아오게 됩니다. 6편에서 이 두 경로를 붙입니다.
기여 — 절차에서 출발하기
작은 패치를 하나 만들어 실제로 보내 보는 방식입니다. 이게 왜 학습 경로인지 의아할 수 있는데, 이유는 이렇습니다. 패치를 보내려면 트리를 받아야 하고, 빌드해야 하고, 코딩 스타일을 맞춰야 하고, 어느 서브시스템 소관인지 알아내야 합니다. 즉 나머지 전부가 부수적으로 따라옵니다. 그리고 리뷰어의 답장은 어떤 책보다 정확한 피드백입니다.
약점은 첫 사이클이 길다는 것입니다. 보내고 답을 받기까지 몇 주가 걸릴 수 있고, 그 사이 아무 일도 일어나지 않습니다. 8편에서 이 경로를 다룹니다.
어느 쪽을 고를 것인가
| 지금 상황 | 권하는 출발점 | 이 시리즈에서 |
|---|---|---|
| 특정 동작의 원인을 코드에서 확인해야 한다 | 읽기 | 2편 → 4편 |
| 서버를 운영하며 커널을 이해하고 싶다 | 관측 | 6편 → 4편 |
| 오픈소스 기여 경험을 만들고 싶다 | 기여 | 3편 → 8편 |
| 드라이버나 모듈을 만들어야 한다 | 읽기 + 모듈 | 3편 → 5편 → 7편 |
| 학습 목적이고 특별한 목표가 없다 | 관측 | 6편 → 2편 → 4편 |
마지막 줄은 제 판단입니다. 목표가 뚜렷하지 않을 때 관측부터 시작하기를 권하는 이유는, 관측은 첫날에 결과가 나오기 때문입니다. 빌드에 실패해 아무것도 못 본 채 이틀을 보내면 대개 거기서 끝납니다.
5. 정말로 필요한 전제 조건
C — 그중에서도 포인터와 구조체
커널 공식 문서인 Documentation/process/howto.rst는 이 부분을 분명하게 씁니다. 커널은 대부분 C로 쓰였고 일부 아키텍처 의존 부분은 어셈블리이며, 커널 개발에는 C에 대한 좋은 이해가 필요하다고 명시합니다. 그리고 커널은 GNU C와 GNU 툴체인으로 작성되며, ISO C11을 따르되 표준에 없는 확장을 여럿 쓴다고 덧붙입니다.
여기서 "C를 안다"가 무엇을 뜻하는지 구체적으로 적어 두겠습니다. 커널 코드에서 실제로 매 페이지 나오는 것은 이 셋입니다.
- 포인터 산술과 구조체 포인터. 커널 코드는 값을 복사하지 않고 포인터로 넘깁니다.
- 함수 포인터 테이블. 커널의 다형성은 전부 구조체 안의 함수 포인터로 구현됩니다. 4편에서 읽을
vfs_read도 결국file->f_op->read를 호출할지file->f_op->read_iter를 호출할지 고르는 코드입니다. - 매크로. 커널은 매크로를 아주 적극적으로 씁니다. 4편에서 볼
SYSCALL_DEFINE3은 함수 정의를 통째로 생성하는 매크로이고, 이걸 모르면sys_read라는 이름을 아무리 grep해도 찾지 못합니다.
빌드 시스템에 대한 감각
make가 무엇인지, 컴파일과 링크가 어떻게 다른지, 헤더가 왜 필요한지 정도면 충분합니다. Kconfig와 Kbuild는 커널만의 물건이라 어차피 새로 배워야 하고, 3편에서 다룹니다.
리눅스를 사용자로서 다뤄 본 경험
dmesg를 읽고, /proc과 /sys가 무엇인지 알고, 패키지를 설치하고, 부트로더 항목을 고칠 수 있으면 됩니다. 이게 없으면 3편의 빌드 단계에서 막힙니다.
시간
정직하게 적겠습니다. 이 시리즈 여덟 편의 실습을 다 해 보려면 주말 몇 번은 필요합니다. 커널 빌드 한 번, QEMU 부팅 한 번, 모듈 하나, 패치 하나가 각각 반나절짜리 일입니다.
6. 필요하지 않은 것
이쪽이 더 중요할 수 있습니다. 없어도 되는 것을 준비하느라 시작을 미루는 경우가 많기 때문입니다.
어셈블리 전문성은 필요 없습니다. 어셈블리가 나오는 곳은 아키텍처별 진입 경로와 부팅 초기, 그리고 일부 원자적 연산 정도입니다. 4편에서 시스템 콜을 따라갈 때도 어셈블리 구간은 "여기서 CPU가 특권 수준을 바꾸고 C 함수로 점프한다"는 한 줄로 넘어갑니다. 그 이상이 필요해지는 것은 아키텍처 코드를 직접 건드릴 때인데, 그건 한참 뒤의 일입니다.
운영체제 이론을 전부 외울 필요는 없습니다. 필요할 때 찾아보면 됩니다. 이 블로그의 운영체제 개념 시리즈가 그 용도로 있습니다. 페이지 테이블이 무엇인지 모르는 채로 mm/을 읽으면 당연히 막히지만, 막힌 다음에 찾아 읽어도 늦지 않습니다.
전용 하드웨어는 필요 없습니다. 3편에서 다루겠지만 커널 실험은 QEMU 안에서 하는 것이 원칙이고, 이는 하드웨어가 없어서가 아니라 그게 옳은 방법이기 때문입니다. 노트북 한 대면 충분합니다.
학위나 자격증은 필요 없습니다. 커널 커뮤니티가 보는 것은 패치입니다.
C++와 러스트도 지금은 필요 없습니다. 커널에 러스트가 들어와 있는 것은 사실이지만, 앞서 세어 본 대로 전체의 0.4퍼센트입니다. 러스트로 커널에 들어가겠다는 계획은 가능하긴 하지만 진입로가 훨씬 좁습니다.
표준 C 라이브러리 지식은 오히려 덜어 내야 합니다. 같은 문서가 이 점을 분명히 합니다. 커널은 프리스탠딩 C 환경이라 표준 C 라이브러리에 의존하지 않으며, 그래서 표준의 일부는 지원되지 않고 임의의 long long 나눗셈과 부동소수점은 허용되지 않습니다. printf도 malloc도 없습니다. 각각에 해당하는 커널 함수가 따로 있고, 규칙도 다릅니다. 7편이 그 규칙을 통째로 다룹니다.
7. 여덟 편을 어떤 순서로 볼 것인가
이 시리즈는 1편부터 8편까지 순서대로 읽도록 썼지만, 5절에서 고른 경로에 따라 건너뛰어도 됩니다.
- 어디서 시작하는가 (이 글) — 지도와 전제 조건
- 소스 받기와 길 찾기 — 트리 종류, 디렉터리 지도, 동작을 구현한 코드를 찾아내는 기술
- 빌드와 부팅 — config, 빌드, 설치, 그리고 QEMU가 선택이 아닌 이유
- 시스템 콜 하나를 끝까지 —
read를 진입점부터 반환까지 따라가기 - 모듈 작성 — 가장 작은 모듈, 파라미터, GPL 심볼
- 관측과 디버깅 — dmesg, ftrace, kprobes, perf, QEMU와 GDB
- 메모리와 동시성 규칙 — 커널 C가 응용 C와 다른 지점
- 첫 패치 — checkpatch부터 메일링 리스트까지
빌드를 하고 싶지 않다면 2편과 4편과 6편만으로도 상당한 것을 얻을 수 있습니다. 반대로 기여가 목표라면 3편과 8편이 필수이고 나머지는 필요할 때 돌아오면 됩니다.
흔한 함정
최신 메인라인을 받아 놓고 오래된 책을 따라간다. 3절에서 든 do_syscall_64 사례가 정확히 이 경우입니다. 책이 다루는 버전과 손에 있는 트리의 버전이 다르면, 함수가 사라진 것처럼 보이는 순간이 반드시 옵니다. 해법은 두 가지입니다. 책이 다루는 버전의 태그를 체크아웃하거나, 이름으로 찾는 습관을 들이는 것입니다. 2편에서 후자를 다룹니다.
호스트 머신에 새 커널을 설치하고 재부팅한다. 3편에서 다시 강조하겠지만, 부팅되지 않는 커널은 부팅되지 않는 컴퓨터입니다. 복구가 불가능하지는 않지만 초심자가 밤 11시에 할 일은 아닙니다.
드라이버부터 읽기 시작한다. 전체의 60퍼센트가 드라이버이니 자연스러운 선택처럼 보이지만, 드라이버는 커널 내부 API를 이미 안다는 전제로 쓰인 코드입니다. 순서가 반대입니다.
규모에 압도되어 준비만 계속한다. C 책을 다시 보고, 운영체제 책을 다시 보고, 어셈블리를 조금 배우다가 정작 트리는 받지 않는 상태입니다. 6절이 이 함정을 겨냥해 쓴 절입니다.
한 번에 커널 전체를 이해하려 한다. 커널 서브시스템은 각자 별도의 관리자와 메일링 리스트, 별도의 git 트리를 가지고 사실상 독립된 프로젝트처럼 굴러갑니다. 스케줄러를 잘 아는 사람이 파일시스템에 대해서는 초심자인 것이 정상입니다.
마치며
커널 공부에서 가장 큰 장벽은 난이도가 아니라 규모에서 오는 마비라고 생각합니다. 4,300만 줄이라는 숫자를 보면 어디를 잡아도 손해 보는 느낌이 들고, 그래서 아무 데도 잡지 않게 됩니다.
이 글에서 하고 싶었던 말은 그 숫자가 실제로는 훨씬 작다는 것입니다. 60퍼센트는 남의 드라이버이고, 11퍼센트는 다른 CPU의 코드이며, 실제로 사람들이 "커널 내부"라고 부르며 읽는 영역은 전체의 몇 퍼센트에 불과합니다. 그리고 그 몇 퍼센트조차 한 번에 읽는 것이 아니라, 질문 하나를 들고 들어가 필요한 파일 몇 개만 여는 방식으로 읽습니다.
다음 편에서는 트리를 실제로 받고, 그 안에서 원하는 코드를 찾아내는 기술을 다룹니다. 이 시리즈에서 가장 실용적인 한 편을 꼽으라면 저는 2편을 고르겠습니다. 찾을 수 있으면 나머지는 시간 문제이고, 찾지 못하면 나머지가 전부 막히기 때문입니다.
이 글의 숫자와 경로는 2026년 8월 19일에 직접 확인한 것입니다. 커널은 계속 움직이므로, 시간이 지난 뒤에 읽는다면 같은 방법으로 다시 세어 보시기 바랍니다. 세는 방법 자체가 2편의 내용이기도 합니다.
직접 해보기
- 리눅스 터미널 — 트리를 받기 전에 셸 감각부터 확인하기
- 커널 파라미터 탐색기 — 커널이 노출하는 설정 표면을 먼저 훑어보기
- 리눅스 명령어 퀴즈 — 3편 빌드 실습에 필요한 명령 복습
다음 편
참고 자료
모든 링크는 2026년 8월 19일에 확인했습니다.
- The Linux Kernel Archives — 이 글 기준 메인라인 7.2, longterm 6.18/6.12/6.6/6.1/5.15/5.10
- HOWTO do Linux kernel development — C 요구 수준, GNU C 확장, 프리스탠딩 환경, 릴리스 주기
- Linux kernel coding style
- Linux kernel licensing rules
- Linux kernel documentation — 공식 문서 트리 전체
- KernelNewbies — 입문자용 커뮤니티와 자료