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

- 모놀리식 커널
- 커널 모드에서 실행되는 단일 대형 프로세스
- 메모리 관리, 프로세스 관리, 디바이스 드라이버 등 핵심 운영체제 서비스를 모두 제공
- 모든 핵심 운영체제 서비스가 동일한 주소 공간에 있다
- 따라서, 서로 통신 및 데이터 공유가 쉽고 빠르다
- 대신, 전체 커널에 영향을 주지 않고 기능을 추가 및 제거하는 것이 어렵다
- 마이크로 커널
- 메모리 관리, 프로세스 통신 등 기본적인 서비스만 제공하는 작은 커널
- 파일 시스템, 디바이스 드라이버 등 기타 서비스는 별도의 유저 모드 프로세스로 실행됨
- 따라서, 전체 커널에 영향을 주지 않고 쉽게 기능을 추가 및 제거할 수 있다
- 또한, 한 서비스의 버그나 취약성이 전체 커널을 손상시키지 않는다
- 그러나, 서비스들이 별도의 유저 모드 프로세스로 실행되므로 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 .config로 make 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
cd busybox-1.36.0
make menuconfig
- menuconfig에 들어가 Settings > Build static binary를 체크해준 뒤
make install CONFIG_PREFIX=../busybox_install_result
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로 모듈이 정상 동작하는 것을 볼 수 있다