在 QEMU 上启动最小的操作系统
必要条件⌗
- QEMU
- Kernel
- BusyBox
QEMU⌗
QEMU 是一个通用的开源机器模拟器和虚拟器,支持 x86 和 ARM 架构的芯片,安装方式可以参考官方文档。
我是在 M1 Max 上使用 Homebrew 安装的:
brew install qemu
qemu-system-x86_64 --version
QEMU emulator version 7.1.0
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
Kernel⌗
可以从现有的 Linux 系统中的 /boot
目录中获取,例如我在阿里云的服务器上得到如下版本的内核:
ls -la /boot
total 133652
drwxr-xr-x 3 root root 4096 Nov 23 2021 .
drwxr-xr-x 19 root root 4096 Jan 24 2022 ..
-rw------- 1 root root 4736015 Apr 21 2020 System.map-5.4.0-26-generic
-rw------- 1 root root 4755132 Nov 6 2021 System.map-5.4.0-91-generic
-rw-r--r-- 1 root root 237718 Apr 21 2020 config-5.4.0-26-generic
-rw-r--r-- 1 root root 237884 Nov 6 2021 config-5.4.0-91-generic
drwxr-xr-x 4 root root 4096 Nov 23 2021 grub
lrwxrwxrwx 1 root root 27 Nov 23 2021 initrd.img -> initrd.img-5.4.0-91-generic
-rw-r--r-- 1 root root 51236625 Nov 23 2021 initrd.img-5.4.0-26-generic
-rw-r--r-- 1 root root 52180616 Nov 23 2021 initrd.img-5.4.0-91-generic
lrwxrwxrwx 1 root root 27 Nov 23 2021 initrd.img.old -> initrd.img-5.4.0-26-generic
lrwxrwxrwx 1 root root 24 Nov 23 2021 vmlinuz -> vmlinuz-5.4.0-91-generic
-rw------- 1 root root 11657976 Apr 21 2020 vmlinuz-5.4.0-26-generic
-rw------- 1 root root 11784448 Nov 6 2021 vmlinuz-5.4.0-91-generic
lrwxrwxrwx 1 root root 24 Nov 23 2021 vmlinuz.old -> vmlinuz-5.4.0-26-generic
我将 vmlinuz-5.4.0-91-generic
内核下载到本地。
BusyBox⌗
BusyBox 是嵌入式 Linux 的瑞士军刀,提供了常用的各种命令,如 ls, cat 等命令。可以在官网的下载列表中找到自己需要的版本,我这里下载的是最新的 1.35.0-x86_64-linux-musl
。
$ wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
--2022-11-27 14:12:08-- https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
Resolving busybox.net (busybox.net)... 140.211.167.122
Connecting to busybox.net (busybox.net)|140.211.167.122|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1131168 (1.1M)
Saving to: ‘busybox’
busybox 100%[====================================================>] 1.08M 50.2KB/s in 17s
2022-11-27 14:12:27 (64.2 KB/s) - ‘busybox’ saved [1131168/1131168]
注意一定要给
busybox
可执行的权限,否则会导致内核 init 时失败!
创建 Makefile⌗
.PHONY: initramfs run clean
$(shell mkdir -p build)
initramfs:
@cd initramfs && find . -print0 | cpio --null -ov --format=newc | gzip -9 \
> ../build/initramfs.cpio.gz
run:
@qemu-system-x86_64 \
-nographic \
-serial mon:stdio \
-m 128 \
-kernel vmlinuz \
-initrd build/initramfs.cpio.gz \
-append "console=ttyS0 quiet acpi=off"
clean:
@rm -rf build
创建 init 执行的命令⌗
在 initramfs 目录中创建 init 脚本文件:
#!/bin/busybox sh
c1="arch ash base64 cat chattr chgrp chmod chown conspy cp cpio cttyhack date dd df dmesg dnsdomainname dumpkmap echo ed egrep false fatattr fdflush fgrep fsync getopt grep gunzip gzip hostname hush ionice iostat ipcalc kbd_mode kill link linux32 linux64 ln login ls lsattr lzop makemime mkdir mknod mktemp more mount mountpoint mpstat mt mv netstat nice nuke pidof ping ping6 pipe_progress printenv ps pwd reformime resume rev rm rmdir rpm run-parts scriptreplay sed setarch setpriv setserial sh sleep stat stty su sync tar touch true umount uname usleep vi watch zcat"
c2="[ [[ awk basename bc beep blkdiscard bunzip2 bzcat bzip2 cal chpst chrt chvt cksum clear cmp comm crontab cryptpw cut dc deallocvt diff dirname dos2unix dpkg dpkg-deb du dumpleases eject env envdir envuidgid expand expr factor fallocate fgconsole find flock fold free ftpget ftpput fuser groups hd head hexdump hexedit hostid id install ipcrm ipcs killall last less logger logname lpq lpr lsof lspci lsscsi lsusb lzcat lzma man md5sum mesg microcom mkfifo mkpasswd nc nl nmeter nohup nproc nsenter nslookup od openvt passwd paste patch pgrep pkill pmap printf pscan"
c3="pstree pwdx readlink realpath renice reset resize rpm2cpio runsv runsvdir rx script seq setfattr setkeycodes setsid setuidgid sha1sum sha256sum sha3sum sha512sum showkey shred shuf smemcap softlimit sort split ssl_client strings sum sv svc svok tac tail taskset tcpsvd tee telnet test tftp time timeout top tr traceroute traceroute6 truncate ts tty ttysize udhcpc6 udpsvd unexpand uniq unix2dos unlink unlzma unshare unxz unzip uptime users uudecode uuencode vlock volname w wall wc wget which who whoami whois xargs xxd xz xzcat yes"
for cmd in $c1 $c2 $c3; do
/bin/busybox ln -s /bin/busybox /bin/$cmd
done
mkdir -p /proc && mount -t proc none /proc
mkdir -p /sys && mount -t sysfs none /sys
export PS1='(linux) '
/bin/busybox sh
Linux 内核通过 init 创建第一个进程,并将控制权交给这个 PID 为 1 的进程,在现代的操作系统中,大多数的 init 进程是 systemd。而 systemd 将使用 syscall
创造整个计算机世界,包括你运行的每个进程!
最终的目录结构⌗
tree
.
├── Makefile # 自动化脚本
├── initramfs # 初始化文件系统,用于映射到 Linux 内存中的文件系统
│ ├── bin
│ │ └── busybox # Unix 工具集
│ └── init # 初始化进程的脚本
└── vmlinuz # Linux Kernel
2 directories, 4 files
生成 initramfs 镜像⌗
$ make
.
./init
./bin
./bin/busybox
2215 blocks
$ tree
.
├── Makefile
├── build
│ └── initramfs.cpio.gz
├── initramfs
│ ├── bin
│ │ └── busybox
│ └── init
└── vmlinuz
3 directories, 5 files
make 主要的逻辑是使用 cpio
命令将 initramfs
目录打包。
启动系统⌗
$ make run
启动完成后会变可以看到一个 Linux Shell 窗口:
到此,一个 minimal 的 Linux 系统就启动了,而他的总大小也只有 13.6 MB。
I hope this is helpful, Happy hacking…