编译环境为ubuntu 18.04

编译kernel

安装编译工具

1
sudo apt install bc binutils bison dwarves flex gcc git gnupg2 gzip libelf-dev libncurses5-dev libssl-dev make openssl pahole perl-base rsync tar xz-utils

下载并编译内核代码

1
2
3
4
5
6
7
wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.9.229.tar.xz
tar xvf linux-4.9.229.tar.xz
cd linux-4.9.229/
export ARCH=x86
make x86_64_defconfig
make menuconfig
make

令编译的内核支持ramdisk驱动

1
2
3
4
5
6
7
8
9
10
11
General setup  --->

[*] Initial RAM filesystem and RAM disk (initramfs/initrd) support

Device Drivers --->

[*] Block devices --->

<*> RAM block device support

(65536) Default RAM disk size (kbytes)

Busybox

编译Busybox

1
2
3
4
tar xvf busybox-1.30.0.tar.bz2
cd busybox-1.30.0/
make menuconfig
make

把busybox配置为静态编译,这样busybox在运行的时候就不需要额外的动态链接库了。

1
2
3
Busybox Settings  --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)

配置根文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd _install/
mkdir etc dev mnt
mkdir -p proc sys tmp mnt
mkdir -p etc/init.d
vim etc/fstab
vim etc/init.d/rcS
chmod 755 etc/init.d/rcS
vim etc/inittab
chmod 755 etc/inittab
cd dev/
sudo su
mknod console c 5 1 #mknod [选项] [名称] [类型] [主设备号] [次设备号]
mknod null c 1 3
mknod tty1 c 4 1

其中, fstab文件:

1
2
3
proc        /proc           proc         defaults        0        0
tmpfs /tmp tmpfs   defaults 0 0
sysfs /sys sysfs defaults 0 0

rcS文件:

1
2
3
4
5
6
7
8
echo -e "Welcome to tinyLinux"
/bin/mount -a #挂载 /etc/fstab 文件中定义的所有文件系统。-a 参数表示挂载所有在 /etc/fstab 文件中列出的文件系统。
echo -e "Remounting the root filesystem"
mount -o remount,rw / #以读写模式重新挂载根文件系统(通常是 /),使其可写入。-o remount,rw 参数表示重新挂载为读写模式。
mkdir -p /dev/pts #挂载伪终端文件系统 (devpts) 到 /dev/pts 目录。-t devpts 参数指定文件系统类型为 devpts。
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug #把/sbin/mdev写到/proc/sys/kernel/hotplug文件里,当有热插拔事件产生时,内核会调用/proc/sys/kernel/hotplug文件里指定的应用程序来处理热插拔事件
mdev -s #启动 mdev 守护进程,它用于在 Linux 系统中自动创建和管理设备节点。-s 参数表示以守护进程模式运行。

mdev是busybox提供的一个工具,相当于简化版的udev。通过描述sysfs下的dev节点,在系统启动和热插拔或动态加载驱动程序时,自动创建设备节点。文件系统中的/dev目录下的设备节点都是由mdev创建的。

inittab文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Format for each entry: <id>:<runlevels>:<action>:<process>
# <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
# <runlevels>: The runlevels field is completely ignored.
# <action>: Valid actions include: sysinit, wait, once, respawn, askfirst, shutdown, restart and ctrlaltdel.
# <process>: Specifies the process to be executed and it's command line.

# Note below that we prefix the shell commands with a "-" to indicate to the
# shell that it is supposed to be a login shell. Normally this is handled by
# login, but since we are bypassing login in this case, BusyBox lets you do
# this yourself...

::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
::askfirst:-/bin/sh
::ctrlaltdel:/bin/umount -a -r

烧录镜像文件

将如下内容拷贝至make_rootfs.sh文件中:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
rm -rf rootfs.ext3
rm -rf fs
dd if=/dev/zero of=./rootfs.ext3 bs=1M count=32
mkfs.ext3 rootfs.ext3
mkdir fs
mount -o loop rootfs.ext3 ./fs
cp -rf ./_install/* ./fs
umount ./fs
gzip --best -c rootfs.ext3 > rootfs.img.gz

运行脚本

1
sh make_rootfs.sh

运行系统

安装qemu

1
apt install qemu

启动qemu

1
qemu-system-x86_64   -kernel ./linux-4.9.229/arch/x86_64/boot/bzImage    -initrd ./busybox-1.30.0/rootfs.img.gz     -append "root=/dev/ram init=/linuxrc"    -serial file:output.txt  -curses
  • -curses: Normally, QEMU uses SDL to display the VGA output. With this option, QEMU can display the VGA output when in text mode using a curses/ncurses interface. Nothing is displayed in graphical mode.-curses works over ssh.
  • -serial:用于配置虚拟机的串行端口。该选项可以用于将虚拟机与主机之间建立串行通信连接,以便在虚拟机中模拟串行设备的输入和输出。使用 -serial file:filename 将串行输出保存到文件中进行分析。以下是一些常用的 -serial 选项参数:
    • stdio:将虚拟机的串行输入输出重定向到主机的标准输入输出(通常是终端窗口)。
    • file:filename:将虚拟机的串行输出重定向到指定的文件中。
    • tcp:host:port:通过 TCP/IP 连接将虚拟机的串行输入输出重定向到指定的主机和端口。
    • udp:host:port:通过 UDP 连接将虚拟机的串行输入输出重定向到指定的主机和端口。
  • -append:用于指定要传递给内核的命令行参数。当使用 -kernel 选项加载一个内核映像时,通常需要将一些参数传递给内核,以配置虚拟机的启动行为。这些参数可以通过 -append 选项来指定。

在生成的终端中关闭系统, 即可退出qemu

1
poweroff