必要条件

  • 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 窗口:

Linux Shell

到此,一个 minimal 的 Linux 系统就启动了,而他的总大小也只有 13.6 MB。

Disk Usage

I hope this is helpful, Happy hacking…