커널 개발을 위해 알아야 할 것

Sijin·2025년 10월 19일

리눅스 커널

리눅스 운영체제와 리눅스 커널

  • 리눅스 운영체제는 컴퓨터가 실행할 수 있는 완전한 플랫폼을 제공하는 소프트웨어
  • 리눅스 커널은 운영체제 구성요소 중 하나로
    • 하드웨어 같은 시스템 리소스를 제어 및 관리하고
    • 소프트웨어에 리소스를 분배하는 서비스를 제공하는
    • 운영체제의 핵심, 기본적인 구성요소이다

모놀리식 커널, 마이크로 커널

  • 모놀리식 커널
    • 커널 모드에서 실행되는 단일 대형 프로세스
    • 메모리 관리, 프로세스 관리, 디바이스 드라이버 등 핵심 운영체제 서비스를 모두 제공
    • 모든 핵심 운영체제 서비스가 동일한 주소 공간에 있다
      • 따라서, 서로 통신 및 데이터 공유가 쉽고 빠르다
      • 대신, 전체 커널에 영향을 주지 않고 기능을 추가 및 제거하는 것이 어렵다
  • 마이크로 커널
    • 메모리 관리, 프로세스 통신 등 기본적인 서비스만 제공하는 작은 커널
    • 파일 시스템, 디바이스 드라이버 등 기타 서비스는 별도의 유저 모드 프로세스로 실행됨
      • 따라서, 전체 커널에 영향을 주지 않고 쉽게 기능을 추가 및 제거할 수 있다
      • 또한, 한 서비스의 버그나 취약성이 전체 커널을 손상시키지 않는다
      • 그러나, 서비스들이 별도의 유저 모드 프로세스로 실행되므로 IPC를 통해 서로 통신해야해 성능이 비교적 느림
  • 리눅스는 모놀리식 커널 방식이다

리눅스 커널 관리 및 개발

  • 리눅스 커널 개발 프로세스는 아래와 같다
    • 먼저 검토를 위해 메일링 리스트에 패치 형식으로 코드 변경 사항을 제출한다
    • 이러한 패치는 관리자 및 기타 개발자가 검토해 피드백을 제공한다
    • 패치가 검토되고 필요에 따라 수정된다
    • 이후 리눅스 커널 기본 개발 분기에 병합된다
    • 리눅스 커널은 2~3개월마다 릴리즈된다
  • 리눅스 커널 코드는 kernel.org 혹은 git에서 받을 수 있다

커널 개발 환경 설정

러스트 툴체인 설치

 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • 러스트 툴체인을 다운로드, 설치하자

리눅스 커널 다운로드

git clone https://github.com/Rust-for-Linux/linux.git
  • rust for linux 프로젝트에서는 개발이 한창 진행되고 있는 러스트 커널이 다양하게 반영되어있다
  • rust 디렉토리를 보면
    • C로 작성된 심볼을 러스트 코드에서 접근해 사용하도록 바인딩하는 wrapper 코드들이다
    • 리눅스 커널은 C로 이미 충분히 안정화 되어있어, 모든 코드를 러스트로 바꾸는 것보다 기존 코드를 활용하는 것으로 판단되었다
  • 리눅스 커널 드라이버 모듈들은 리눅스 커널에서 노출하는 심볼에만 접근가능하다
    • 러스트로 개발된 커널 모듈들은 이렇게 노출된 심볼에 바로 접근할 수 없다
    • 따라서 wrapper 코드가 필요하다

리눅스 커널 빌드

make rustavailable
  • 위 명령어로 러스트 바이너리 버전이 올바른지 확인할 수 있다.
  • 오류가 발생하는 경우
rustup override set 1.83.0
cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli
rustup component add rust-src
  • 위 명령어로 버전에 맞는 바이너리를 설치할 수 있다
make mrproper
make rustavailable
make defconfig
make menuconfig
  • menuconfig에 들어가 커널이 러스트를 지원하도록 선언해야한다
    • General setup -> Rust support
  • Rust support가 없는 경우
┌───────────────────────────────────────────── Search Results ──────────────────────────────────────────────┐
  │ Symbol: RUST [=y]                                                                                         │
  │ Type  : bool                                                                                              │
  │ Defined at init/Kconfig:2087                                                                              │
  │   Prompt: Rust support                                                                                    │
  │   Depends on: HAVE_RUST [=y] && RUST_IS_AVAILABLE [=y] && (!MODVERSIONS [=n] || GENDWARFKSYMS [=n]) && !G │
  │   Location:                                                                                               │
  │     -> General setup                                                                                      │
  │ (1)   -> Rust support (RUST [=y])                                                                         │
  │ Selects: EXTENDED_MODVERSIONS [=n] && CFI_ICALL_NORMALIZE_INTEGERS [=n]                                   │
  • /로 rust를 검색해보면 위와 같이 검색된다
  • Rust support 항목은 Depends on의 조건들이 만족해야 보여진다
  • search result에서는 depends on이 잘려서, Kconfig 파일을 확인해보면 아래와 같다
config RUST
	bool "Rust support"
	depends on HAVE_RUST
	depends on RUST_IS_AVAILABLE
	select EXTENDED_MODVERSIONS if MODVERSIONS
	depends on !MODVERSIONS || GENDWARFKSYMS
	depends on !GCC_PLUGIN_RANDSTRUCT
	depends on !RANDSTRUCT
	depends on !DEBUG_INFO_BTF || (PAHOLE_HAS_LANG_EXCLUDE && !LTO)
	depends on !CFI || HAVE_CFI_ICALL_NORMALIZE_INTEGERS_RUSTC
	select CFI_ICALL_NORMALIZE_INTEGERS if CFI
	depends on !CALL_PADDING || RUSTC_VERSION >= 108100
	depends on !KASAN_SW_TAGS
	depends on !(MITIGATION_RETHUNK && KASAN) || RUSTC_VERSION >= 108300
help
	Enables Rust support in the kernel.
  • grep RUST .configmake defconfig 단계에서 설정된 값들을 확인할 수 있다
  • 나의 경우엔 RUSTC_VERSION이 낮아서 Rust Support가 보이지 않았었다
  • 설정 후 make로 커널 빌드를 수행할 수 있다

qemu 환경에서 실행

qemu-system-x86_64 -kernel arch/x86_64/boot/bzImage -nographic -append "console=ttyS0"
  • 위 명령어로 qemu 환경에서 실행할 수 있다
[    3.876691] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---
  • 처음에 qemu 환경에서 커널을 부팅하면 위 메세지와 같이 panic이 발생한다
  • 이는 스토리지 디바이스 마운트 실패로 인한 문제로, rootfs를 지정하지 않아 발생하는 문제이다
  • 우리는 initramfs라는 ram 기반의 루트 파일 시스템 이미지를 만들어 사용하자

buxybox 빌드

wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar -xjvf busybox-1.36.0.tar.bz2
  • 우선 busybox를 받아준다
cd busybox-1.36.0
make menuconfig
  • menuconfig에 들어가 Settings > Build static binary를 체크해준 뒤
make install CONFIG_PREFIX=../busybox_install_result
  • busybox를 빌드한다

initramfs 생성

mkdir -p initramfs
cd initramfs
mkdir -p bin sbin etc proc sys usr/bin usr/sbin
cp -a ../busybox_install_result/* ./
  • 위 명령어로 루트 파일 시스템 디렉토리 구조를 만들고 buxybox 결과물을 가져온다
#!/bin/sh
echo "+++ /init starting +++" > /dev/console
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
echo "+++ /bin/mount starting +++" > /dev/console
/bin/mount -t devtmpfs devtmpfs /dev
/bin/mount -t proc none /proc
/bin/mount -t sysfs none /sys
echo "+++ /bin/sh starting +++" > /dev/console
exec /bin/sh
  • 리눅스 커널은 루트 파일 시스템을 마운트 하고 먼저 /init을 실행한다
    • 여기서는 환경을 위한 몇가지 디렉토리를 마운트하고 shell을 실행한다
  • initramfs/init 파일을 만들고 위 처럼 작성했다
find . -print0 | cpio --null -ov -H newc | gzip -9 > ../initramfs.cpio.gz
  • 그 후 위 명령어로 initramfs 이미지를 생성한다
    • find . -print0으로 현재 디렉토리 아래 모든 파일과 디렉토리를 찾는다
      • -print0 옵션으로 각 파일 이름을 \0으로 구분한다
    • find의 결과를 cpio --null -ov -H newc로 넘겨, cpio 아카이브 파일을 생성한다
    • 그 후 gzip -9 > ../initramfs.cpio.gz를 통해 압축하고 ../initramfs.cpio.gz 파일에 덧붙힌다

리눅스 커널 실행

qemu-system-x86_64 \
  -kernel arch/x86_64/boot/bzImage \
  -initrd ../initramfs.cpio.gz \
  -nographic \
  -append "console=ttyS0 rdinit=/init" \
   -virtfs local,path=../kernel_module,security_model=none,mount_tag=rust_modules
  • 리눅스 커널을 실행하면 아래와 같이 sh가 실행되어있다
~ #
~ # ls
bin      etc      linuxrc  root     sys
dev      init     proc     sbin     usr
~ #
~ #
  • -virtfs로 지정한 외부 디렉토리를 마운트하면 qemu 외부의 kernel_modules 디렉토리와 바인딩된 디렉토리를 만들 수 있다
~ # mkdir mnt
~ # mount -t 9p -o trans=virtio rust_modules ./mnt
[  186.765469] mount (62) used greatest stack depth: 13712 bytes left

rust 커널 모듈 빌드, 실행

샘플 커널 모듈 빌드

  • make menuconfig > Kernel hacking > sample kernel code > rust samples에 들어가서 빌드하고자 하는 샘플 코드를 선택할 수 있다
    • [*] 이 표시는 커널 이미지에 바이너리를 포함시킨다는 의미이고
    • [M] 이 표시는 별도 모듈 파일로 빌드한다는 의미이다
    • 우리는 모듈 파일로 빌드해보자
linux/samples/rust$ ls *.ko
rust_debugfs.ko         rust_dma.ko               rust_driver_faux.ko  rust_driver_platform.ko  rust_misc_device.ko
rust_debugfs_scoped.ko  rust_driver_auxiliary.ko  rust_driver_pci.ko   rust_minimal.ko          rust_print.ko
  • 커널을 다시 빌드하면 위와 같이 ko 파일들이 생성된다
  • 빌드된 ko 파일들을 kernel_modules 디렉토리로 옮겨 qemu에서 확인할 수 있도록 하자
~ # cd mnt/
/mnt # ls
rust_debugfs.ko           rust_driver_faux.ko       rust_misc_device.ko
rust_debugfs_scoped.ko    rust_driver_pci.ko        rust_print.ko
rust_dma.ko               rust_driver_platform.ko   test
rust_driver_auxiliary.ko  rust_minimal.ko
/mnt # insmod rust_minimal.ko
[   44.910032] rust_minimal: Rust minimal sample (init)
[   44.910763] rust_minimal: Am I built-in? false
/mnt #
/mnt # lsmod
rust_minimal 12288 0 - Live 0xffffffffc0400000
  • qemu 환경에서 insmod, lsmod로 모듈이 정상 동작하는 것을 볼 수 있다

0개의 댓글