- Today
- Total
Byeo
Linux Kernel Compile + QEMU 테스트 본문
개요
실제 리눅스 네트워킹 스택을 깊게 파보고 싶다는 생각은 있었으나 실천은 못하고 있던 찰나, 적절히 참고할만한 블로그를 발견해서 한 번 진행해보려고 합니다.
가장 먼저 해보려고 하는 것은 깊이 들여다보기 전에, 테스트 환경을 갖춰보려고 해요. 그래서 직접 커널 코드를 수정해 보면서 알아갈 수 있도록 해보려고 합니다.
포스트를 계속 이어가면서, 제가 리눅스 커널 코드를 아주 간단하게 수정하고 빌드했을 때 올바르게 반영이 되는지까지 확인해보겠습니다.
내용
과정은 크게 2가지입니다. 커널 빌드, rootfs 생성.
환경
약 4년 전, 대학원 입학 전에 구매했던 갤럭시 이온 노트북이 이제는 놀고 있습니다. 여기에 Ubuntu 멀티부팅을 세팅하여 진행하였습니다.
- 물리 코어 4개, 논리 코어 8개 (하이퍼스레드)
- Core i5-10210
- Ram 8 GB
- 저장공간 80GB (윈도우와 멀티부팅)
1. 커널 빌드
필요 패키지 설치
sudo apt-get update
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison qemu-system-x86
우리가 수정한 Linux kernel 코드를 실행시킬 qemu를 설치해 줍니다.
Linux kernel Source
wget https://github.com/torvalds/linux/archive/refs/tags/v5.15.tar.gz
tar -xvzf v5.15.tar.gz
cd linux-v5.15
Ubuntu 22.04에서 주로 사용하는 Linux 5.15.0을 기준으로 진행해 보겠습니다. 만약 다른 버전을 원하시는 경우에는 위의 링크를 바꿔주시거나 https://github.com/torvalds/linux/tags 리눅스 github repository를 가셔서 고르시면 되겠습니다.
Configuration File
make defconfig
make kvm_guest.config
원 블로그에서는 make kvmconfig를 사용하라고 안내하지만 해당 명령어는 linux 5.10을 기점으로 삭제되었다고 합니다. 대신 make kvm_guest.config를 사용하라고 안내하고 있습니다. 공지사항 , github 코드 1 2
root@bklee:/home/bklee/linux-5.15/linux-5.15# cat .config | grep KVM
CONFIG_KVM_GUEST=y
CONFIG_HAVE_KVM=y
# CONFIG_KVM is not set
CONFIG_PTP_1588_CLOCK_KVM=y
make kvm_guest.config를 수행하고 나면. config파일에 KVM 관련 정보가 담겨있는 것을 확인할 수 있습니다.
# Coverage collection.
CONFIG_KCOV=y
# Debug info for symbolization.
CONFIG_DEBUG_INFO=y
# Memory bug detector
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y # bklee: cannot find
# Required for Debian Stretch
CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
...
CONFIG_KGDB=y
상세한 내용은 차차 알아가야겠지만 참조했던 블로그에서 안내하는 대로 Debug관련 configuration도 활성화해줍시다.
make olddefconfig
수정했던 configuration을 반영하여 .config를 재생성하기 위해 위를 실행해 줍니다.
Make
make -j`nproc`
nproc은 CPU의 코어 수를 출력하는 명령어고, make의 -j option은 주어진 수만큼 병렬로 make를 실행토록 합니다.
코어 8개 기준으로 약 8분 정도 소요되었습니다.
2. Root File System 생성
root file system을 생성하여야 시스템이 올바르게 실행될 수 있습니다. 조사를 해보니 몇 가지 방법이 나뉘는 것 같습니다.
(1) busybox를 이용하여 rootfs 생성: https://jeongzero.oopy.io/73084e52-54fa-43e2-986b-072ee2a4f80d
(2) ubuntu cloudimg rootfs 이용
(3, 추천) debootstrap을 이용해서 생성 (https://vccolombo.github.io/cybersecurity/linux-kernel-qemu-setup/ 참고)
(4) mkinitramfs로 생성: mkinitramfs -o ramdisk.img
(5) buildroot 사용하기: https://tldp.org/HOWTO/Bootdisk-HOWTO/buildroot.html
(1) busybox를 이용하여 rootfs 생성
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xvf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make defconfig
make menuconfig
make -j`nproc`
make install -j`nproc`
cd _install
ls
여기까지 실행해서 ls를 확인해보면 다음과 같이 4개의 폴더가 생긴 것을 확인할 수 있습니다.
root@bklee:/home/bklee/linux-5.15/linux-5.15/image/busybox-1.36.1/_install# ls
bin linuxrc sbin usr
drive 생성하기
dd if=/dev/null of=linux.img bs=1M seek=4096
mkfs.ext4 -F busybox.img
mkdir mnt_tmp
mount -t ext4 -o loop busybox.img mnt_tmp/
cp -R busybox-1.36.1/_install/* mnt_tmp/
umount mnt_tmp
차례로
1) 4 GB짜리 빈 파일 (linux.img) 생성
2) linux.img를 ext4 file system으로 변경
3) 잠시 mount를 시키기 위해서 mnt_tmp 폴더 생성
4) 마운트 실행
5) busybox에서 기본적으로 설치되었던 4개의 directory (bin, linuxrc, sbin, usr)을 복사
6) unmount
실행이 잘 된 것을 확인할 수 있죠. (dev 폴더는 mount 후에 제가 직접 만들었습니다.)
(2) Ubuntu Cloud Image RootFS 이용하기
지금까지는 busybox를 통해서 만들어진 rootfs를 이용해서 qemu를 실행시켜 보았습니다. 대신, 정말 단순한 rootfs다보니까 명령어도 거의 없고 기능도 상당히 제한적입니다.
혹시나 ubuntu를 가져오고 싶은 경우에는 https://cdimage.ubuntu.com/ubuntu-base/releases/jammy/release/ 에서 다운로드 받으면 됩니다.
마찬가지로 mount 한 뒤에 복사를 실행할 때, ubuntu에서 다운로드 받은 root 파일들을 복사하면 되겠죠?
대신, ubuntu rootfs를 복사해서 사용할 때 몇 가지 에러가 발생하더라구요. 우선 깊이 볼 내용은 아니지만 snapd와 systemd-networkd-wait-online에서 자꾸 이슈가 발생해서 systemctl disable systemd-networkd-wait-online을 통해 비활성화 하였습니다.
(3) debootstrap을 이용해서 RootFS 생성하기
apt install debootstrap
mkdir debootstrap && cd debootstrap
debootstrap --include=openssh-server --arch=amd64 jammy .
cd ..
ls debootstrap
root@bklee:/home/bklee/linux-5.15/linux-5.15/image# ls debootstrap
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
약 수 십분을 기다리고 ls를 입력해보면 rootfs가 생성되어있습니다.
dd if=/dev/null of=ubuntu-from-debootstrap.img bs=1M seek=4096
mkfs.ext4 -F ubuntu-from-debootstrap.img
mkdir mnt_tmp/
mount -t ext4 -o loop ubuntu-from-debootstrap.img mnt_tmp/
cp -R debootstrap/* mnt_tmp
umount -t mnt_tmp
이를 mount directory에 복사하여 사용합니다. 개인적으로 이 방법이 가장 깔끔한 듯 합니다.
3. QEMU 실행
#!/bin/bash
qemu-system-x86_64 \
-m 2G \
-smp cores=4 \
-kernel ./arch/x86/boot/bzImage \
-drive file=image/busybox.img,format=raw \
-append "root=/dev/sda rw console=ttyS0" \
-net user,host=10.0.2.10,hostfwd=tcp:127.0.0.1:10021-:22 \
-net nic,model=e1000 \
-enable-kvm \
-nographic \
-pidfile vm.pid \
2>&1 | tee vm.log
를 qemu_run.sh에 저장한 뒤, 실행하면 됩니다! 물론 쉘에 한 줄로 쳐도 괜찮지만, 나중에 실행하기에 번거롭죠. 그래서 커맨드를 저장하고 스크립트를 실행하는 방식으로 하는 것이 더 편리합니다.
chmod +x qemu_run.sh
./qemu_run.sh
(1) busybox로 생성한 qemu vm
(3) debootstrap으로 생성한 qemu vm
qemu 실행 스크립트는 -drive file에서 img 파일만 바꿔주면 됩니다. (image/ubuntu-from-debootstrap.img)
아무 에러 없이 깔끔하게 실행됐네요!
Tip)
qemu를 실행하다가 panic이 발생한 경우, 작동이 멈추는 경우가 있습니다. 이 때에는 터미널을 하나 더 열고 killall qemu-system-x86_64를 입력해주시면 됩니다.
'프로그래밍 (Programming) > 네트워크 스택' 카테고리의 다른 글
socket system call 2 (inet_create) (0) | 2024.03.31 |
---|---|
socket system call 1 (syscall_socket ~ __sock_create) (0) | 2024.03.31 |
추가한 System Call을 함수 명으로 호출하기 (glibc) (0) | 2024.03.22 |
Linux System Call 추가하기 (0) | 2024.03.16 |
VSCode에 Linux Kernel Source Code 환경 구성하기 (0) | 2024.03.12 |