2P by GN⁺ | ★ favorite | 댓글 1개
  • Linux Kernel Module Programming Guide는 Linux v5.10 이상에서 로드 가능 커널 모듈을 만들기 위한 무료 가이드로, 개발 환경부터 빌드·로드·디버깅·주요 커널 인터페이스까지 한 흐름으로 다룸
  • 초반 예제는 hello-*.cmodule_init(), module_exit(), kbuild, insmod, rmmod, dmesg를 익히게 하며, QEMU 기반 devtools로 호스트 시스템 손상 위험을 줄임
  • 커널 모듈은 커널 주소 공간에서 실행되므로 잘못된 포인터, 언로드 순서, 동시성, 사용자 메모리 복사 오류가 커널 메모리 손상이나 시스템 불안정으로 이어질 수 있음
  • 문자 디바이스, /proc, seq_file, threaded IRQ, input, PCI, USB, 블록, 네트워크, Device Model, Device Tree, static key까지 확장하며 등록·해제 순서와 lifetime 관리를 반복적으로 다룸
  • 커널 내부 API는 버전별로 바뀌기 때문에 LINUX_VERSION_CODE, KERNEL_VERSION, CONFIG_MODVERSIONS, SecureBoot 서명, version magic 같은 조건을 확인하며 예제도 조건부 컴파일을 포함함

가이드 구성과 기본 흐름

  • 이 가이드는 GitHub 저장소PDF 문서를 제공하는 커널 모듈 학습 자료이며, Open Software License 3.0 조건으로 복제·수정·배포할 수 있음
  • 현재 가이드는 Linux v5.10을 최소 지원 기준으로 삼고, 장기 지원 커널 전반에서 예제와 지침의 호환성을 유지하는 것을 목표로 함
  • 학습자는 C 언어와 일반 프로세스용 프로그램 작성 경험이 필요하며, 커널 모듈은 동적으로 로드·언로드되어 재부팅 없이 커널 기능을 확장함
  • 기본 개발 흐름은 커널 헤더 설치, make.ko 빌드, modinfo 확인, insmod 로드, dmesg 또는 journalctl -k 로그 확인, rmmod 언로드로 이어짐
  • devtools/는 커널 소스와 BusyBox 루트 파일시스템을 빌드하고 QEMU에서 부팅해, examples/9p virtfs로 공유한 뒤 게스트에서 모듈을 테스트하게 함
  • 모듈 초기화와 정리는 module_init()·module_exit() 사용이 선호되며, 기존 init_module()·cleanup_module() 방식은 x86 IBT가 활성화된 커널 6.15 이후 일부 조건에서 빌드 실패를 일으킬 수 있음
  • 커널 모듈은 printf()나 libc를 쓰지 않고 커널이 내보낸 심벌만 사용할 수 있으며, 출력은 터미널이 아니라 커널 로그 링 버퍼로 감
  • 사용자 공간과 커널 공간 사이의 데이터 이동에는 put_user, get_user, copy_to_user, copy_from_user 같은 전용 함수가 필요함
  • 문자 디바이스 예제는 register_chrdev, file_operations, dynamic major number, /dev 노드 생성, exclusive open, put_user 기반 read, 미지원 write 처리를 보여줌
  • /proc 예제는 proc_create, proc_ops, read/write 콜백, seq_file API를 다루며, Linux v5.6 이후 /proc 핸들러에 file_operations 대신 proc_ops가 도입된 변화를 반영함
  • threaded IRQ는 request_threaded_irq()로 top-half와 bottom-half를 나누며, top-half는 interrupt context에서 최소 작업만 수행하고 IRQ_WAKE_THREAD로 스레드 기반 bottom-half를 깨움
  • 이후 장들은 input, PCI, USB, 블록, 네트워크, Device Model, Device Tree 같은 실제 드라이버 영역으로 확장하며, 각 subsystem의 등록 방식과 userspace ABI 선택을 중심으로 이어짐
  • 최적화와 안전성 부분은 likely·unlikely, static key, 작은 커널 스택, FPU 사용 금지, 초기화되지 않은 padding 유출, double-underscore 내부 API 사용 주의를 다룸

빌드·로드에서 먼저 부딪히는 제약

  • 한 커널용으로 컴파일된 모듈은 다른 커널에서 로드되지 않을 수 있으며, version magic과 CONFIG_MODVERSIONS가 맞지 않으면 Invalid module format이나 심벌 버전 불일치가 발생함
  • 대부분의 일반 Linux 배포판 커널은 modversioning이 활성화되어 있을 수 있어, 예제가 바로 동작하지 않으면 modversioning을 끈 커널이나 QEMU 환경을 고려해야 함
  • SecureBoot가 켜진 시스템에서는 서명되지 않은 모듈 로딩이 제한될 수 있고, Lockdown: insmod: unsigned module loading is restricted가 보이면 SecureBoot 비활성화나 모듈 서명 절차가 필요함

QEMU 기반 실습 환경

  • devtools/setup.sh는 커널 tarball과 BusyBox를 다운로드·빌드하고 initramfs를 패키징함
  • devtools/build-modules.sh는 QEMU 커널 대상 모듈을 빌드하고, devtools/boot.sh는 게스트 셸을 제공하며, devtools/test-modules.sh는 모듈별 insmod·rmmod 자동 테스트를 수행함
  • GDB 디버깅은 LKMPG_NO_PREBUILT=1 devtools/setup.shvmlinux를 빌드한 뒤 devtools/boot.sh --gdb와 원격 GDB 연결로 진행함

커널 코드 작성 규칙

  • init 함수에서는 등록·할당이 실패할 수 있으므로, 획득한 리소스는 goto 기반 오류 경로에서 역순으로 해제해야 함
  • 커널에 콜백 구조를 등록하면 사용자 공간이 init 반환 전에도 콜백을 호출할 수 있으므로, 내부 초기화를 끝낸 뒤 마지막에 등록하고 해제는 먼저 수행하는 register last, unregister first 규칙이 중요함
  • process context, softirq/tasklet context, hardirq context는 sleep, 사용자 메모리 접근, GFP_KERNEL, mutex 사용 가능 여부가 다르며, 이 구분을 잘못 이해하면 흔한 커널 버그로 이어짐

디바이스와 subsystem별 주의점

  • 문자 디바이스는 major number로 드라이버를 식별하고 minor number로 드라이버 내부의 여러 장치를 구분하며, 최신 방식에서는 register_chrdev()보다 cdev 인터페이스가 권장됨
  • PCI 드라이버는 고정 주소를 가정하지 않고 PCI core가 열거한 BAR resource를 매핑하며, Linux 5.10 이후 코드에서는 pcim_enable_device()와 device-managed resource API가 teardown 버그를 줄이는 데 유용함
  • USB 드라이버는 hotplug와 disconnect를 정상 이벤트로 취급해야 하며, URB completion과 disconnect·timeout·suspend·userspace shutdown이 경합할 수 있음을 전제로 설계해야 함
  • 블록 드라이버는 blk-mq, request, gendisk, queue limit, flush/FUA semantics를 중심으로 동작하며, 단순 read/write 콜백이 아니라 비동기 request completion 모델에 참여함
  • 네트워크 드라이버는 struct net_device, net_device_ops, sk_buff, NAPI, offload feature flag, link-state 보고와 결합되며, 잘못된 offload 선언은 트래픽 손상으로 이어질 수 있음

커널 버전 변화 대응

  • 예제는 Linux 6.4의 class_create() 시그니처 변화, Linux v5.6의 proc_ops, Linux 6.11의 remove() 반환 타입 변화, Linux 5.15~6.9 사이 blk-mq helper 변화를 조건부 컴파일로 처리함
  • 커널 내부 인터페이스는 시스템 호출보다 자주 바뀌므로, 여러 커널을 지원하는 모듈은 LINUX_VERSION_CODEKERNEL_VERSION 비교를 피하기 어려움

안전성 체크포인트

  • 커널 스택은 사용자 공간 스택보다 훨씬 작고, 많은 시스템에서 8 KiB 또는 16 KiB 수준일 수 있으므로 큰 배열은 kmalloc()·kzalloc()을 사용해야 함
  • copy_to_user()로 사용자 공간에 데이터를 보낼 때 padding을 포함한 모든 바이트가 초기화되어 있어야 하며, 그렇지 않으면 커널 메모리 정보 유출이 발생할 수 있음
  • __kmalloc(), __list_add()처럼 double underscore로 시작하는 API는 내부 전제조건을 가정할 수 있으므로, 문서가 요구하지 않으면 public wrapper를 우선 사용해야 함

생략된 범위

  • 입력 처리 과정에서 일부 원문 청크가 길이·비용 제한으로 생략됐다고 명시되어 있어, 이 요약은 가이드 전체의 모든 장·예제·코드 경로를 빠짐없이 다루지 않음

댓글과 토론

Hacker News 의견들
  • QEMU는 커널 해킹을 경험해 보기 좋은 방법임
    누군가 LDD(Linux Device Drivers) 와 Linux 커널 책들을 업데이트해 주면 좋겠고, 이런 기술서는 수익을 내기 어려우니 Linux Foundation이 후원해도 좋을 듯함

    • 드라이버 작성과 QEMU로 커스텀 장치를 만들어 붙이는 과정을 조금 써둔 글이 있음: [0] https://blog.davidv.dev/posts/learning-pcie/, [1] https://blog.davidv.dev/posts/pcie-driver-dma/
    • virtme-ng https://github.com/arighi/virtme-ng를 쓰면 QEMU에서 개발 중인 커널을 띄우기가 정말 쉬워짐
    • 콘솔도 없는 초기 단계 커널 디버깅에는 QEMU를 많이 씀
      이번 주에도 v6.8에서 arm64 커널 명령줄 매개변수가 146자 이상이면 커널이 즉시 조용히 멈추는 문제를 QEMU + GDB로 재현하고, Debian 12 Bookworm amd64 호스트에서 arm64 커널 빌드를 에뮬레이션해 문제 코드를 한 줄씩 따라가며 원인을 찾았음
      흐름은 빌드 의존성과 크로스 컴파일 도구가 준비된 환경에서 arm64 커널 이미지와 GDB용 스크립트를 빌드하고, 호스트에 gdb, 필요하면 gdb-multiarch, 그리고 qemu-system-arm을 설치한 뒤, qemu-system-aarch64-S -gdb tcp::1234로 멈춘 상태에서 띄우고 다른 터미널에서 gdb-multiarch ./vmlinux로 붙는 방식임
      이후 GDB에서 target remote :1234, break __parse_cmdline, continue를 실행하면 메모리·변수·스택 확인과 단일 단계 실행 같은 일반 GDB 기능을 쓸 수 있음
      커널 GDB 디버깅과 lx-* 스크립트는 https://www.kernel.org/doc/html/latest/dev-tools/gdb-kernel-... 참고
      lx-* Python 스크립트를 GDB가 쓰려면 보통 echo "add-auto-load-safe-path ${SRC_DIR}/scripts/gdb/vmlinux-gdb.py" > ~/.gdbinit처럼 경로 허용도 필요함
    • 지금 커널에 들어간 WireGuard 테스트 모음은 QEMU로 커널 모듈을 개발하고 자동 테스트까지 해보기 좋은 예제임
    • Greg KH가 LDD 4판은 나오지 않을 거라고 꽤 명확히 말했음
  • 관련 HN 스레드: https://news.ycombinator.com/item?id=35782630, https://news.ycombinator.com/item?id=28283030

  • The Linux Memory Manager도 참고할 만함: https://linuxmemory.org/chapters
    저자가 7월 초에 보낸 마지막 업데이트에 따르면 초고를 끝냈고, 지금은 출판사와 함께 편집 단계에 들어갔다고 함

    • 목차는 좋아 보이는데, 제작을 지원할 예약 구매가 없는 게 아쉬움
  • 몇몇 예제는 직접 실행해 보기 어려워 보임
    예를 들어 “Detecting button presses”는 RPi용 모듈을 빌드할 수 있다고 가정하는데, 그 자체가 크로스 컴파일 같은 작업을 요구해서 간단하지 않을 수 있음

    • 약간 번거롭긴 해도, 그냥 Raspberry Pi에서 컴파일러를 돌리면 되지 않나 싶음
  • 자세하고 실습 중심이며, 바로 커널 모듈을 빌드해 보는 튜토리얼이라 훌륭함

  • 함께 볼 만한 자료: https://0xax.gitbooks.io/linux-insides/content/index.html

  • 파일시스템이나 메모리 관리 같은 Linux 커널 프로그래밍 전반은 어디서 보면 좋을지 궁금함
    오래전에는 Robert Love의 “Linux Kernel Development”가 있었는데, 이제는 업데이트되지 않는 듯함

  • 이걸 처음 읽은 게 약 22년 전이었음 :)