在x86机器上模拟x86,可以使用kvm加速。另外,x86环境中,硬件设备都相对标准(固定),参数设置可以相对简单。
Host环境
host环境为Ubuntu20.04 ,物理机。
1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.5 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.5 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal
qemu 版本7.2.1,从默认配置编译而来
1
2
3
4
# ~/Downloads/qemu-7.2.1/build
$ ./qemu-system-x86_64 --version
QEMU emulator version 7.2.1
Copyright (c) 2003-2022 Fabrice Bellard and the QEMU Project developers
下载系统组件
1
2
3
4
5
6
7
# download kernel
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.241.tar.xz
tar xvf linux-5.4.241.tar.xz
# download busybox
wget https://www.busybox.net/downloads/busybox-1.34.1.tar.bz2
tar xvf busybox-1.34.1.tar.bz2
编译内核
安装内核镜像编译依赖的相关工具,并使用默认配置编译内核,单独使用文件夹build_dir
作为输出目录。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 安装相关依赖工具
$ sudo apt install flex bison libncurses5-dev build-essential
# 进入linux source目录
$ cd linux-5.4.241
# 使用x86_64 默认配置文件 x86_64_defconfig
$ ls arch/x86/configs/
i386_defconfig tiny.config x86_64_defconfig xen.config
# 使用x86默认配置
$ make O=build_dir x86_64_defconfig
# 可以使用 make help 查看帮助
# 另外,可以 make menuconfig 进行一些内核配置和修改
# 使用多核编译,这里有20核
$ make O=build_dir bzImage -j20
initramfs
测试内核menuconfig中集成initramfs和单独制作initrd,qemu可以使用initrd(固定大小,要求内核支持使用的文件系统, 启动参数需要指定 /dev/ram0),但是测试initramfs未成功。
对做好的initramfs 的一些说明,添加或修改 initramfs 根目录下的 /init 文件,作为启动脚本,参考:
1
2
3
4
5
6
7
8
9
10
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mdev -s
echo "hello ,this is my init script..."
/bin/sh
# exec switch_root /mnt /sbin/init # 切换真正的根文件系统,这里没有,需要有pid=1的进程调用
注意:/init
是最先执行的,先于 /etc/inittab
,/etc/init.d/rcS
, 所以挂载 /proc
和 /sys
需要写在 /init
中或者说应该先与 /bin/sh
执行。这个和一般的根文件系统有些差别。另外,这里就运行到initramfs,不切换根文件系统。
另外,可以将这里最后的 /bin/sh
替换为固定的程序,嵌入式环境中可以这么使用。
测试内核集成initramfs
qemu中使用单独的initramfs没有成功,但是将initramfs集成到内核中是可以的。
在内核的make meuconfig中,设置
1
2
3
General setup
Initial RAM filesystem and RAM disk (initramfs/initrd) support
Initramfs source file(s)
为做好的initramfs目录,然后编译 sudo make O=build_dir bzImage -j20
,如果initramfs中有root权限的文件, 编译时可能会因为权限不足失败,所以这里加上sudo运行编译。
使用qemu模拟运行,因为内核中集成了initramfs,所以只需要一个内核即可。
1
2
3
4
5
6
7
8
sudo ./qemu-system-x86_64 \
-machine pc \
-nographic \
-accel kvm \
-smp 4 \
-m 4096M \
-kernel /my/path/to/linux-5.4.241/build_dir/arch/x86/boot/bzImage \
-append "console=ttyS0 init=/init"
使用 -machine help
可以查看支持的设备。内核使用bzImage的文件,路径默认在build_dir/arch/x86/boot/bzImage
, 启动参数中console=ttyS0
,这个需要写,否则没有输出,init指定rootfs中的初始化程序或脚本。
这种方式主要的确定是initramfs和内核是集成在一起的,如果要单独修改其中之一,都需要重新编译。好处是qemu模拟时,不需要执行 initrd文件。
单独编译initrd
使用cpio工具将上述的rootfs打包,在qemu中使用initramfs的形式加载,启动失败,最后提示无法挂载跟文件系统,改用initrd 方式 试一下。该方式需要在内核配置中 File Systems
中,将需要的文件系统编译进内核。一般initrd可以格式化为ext4,内核也是 默认将该文件系统编译进内核的。这样的话基本不需要修改。
内核需要的配置,一定要打开,将其编译进内核
ramdisk的大小和个数根据需要设置即可:
1
2
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)
然后重新编译生成bzImage。
制作ramdisk
做好的rootfs目录为rootfs_my_init_ramfs
。查看一下总大小:
1
2
$ du -sh ./rootfs_my_init_ramfs/
2.7M ./rootfs_my_init_ramfs/
然后制作,这里弄一个4MB大小的ramdisk就够了
1
2
3
4
5
6
7
dd if=/dev/zero of=myramdisk bs=1M count=4
mkfs.ext4 -F myramdisk
mkdir -p mnt_for_loop
sudo mount -t ext4 myramdisk ./mnt_for_loop/ -o loop
sudo cp -arf rootfs_my_init_ramfs/* mnt_for_loop/
sudo umount mnt_for_loop
gzip -9 ./myramdisk
使用qemu可以成功启动
1
2
3
4
5
6
7
8
9
sudo ./qemu-system-x86_64 \
-machine pc \
-nographic \
-accel kvm \
-smp 4 \
-m 4096M \
-kernel /home/pjw/Downloads/linux-5.4.241/build_dir/arch/x86/boot/bzImage \
-append "root=/dev/ram0 rw rootfstype=ext4 console=ttyS0 init=/linuxrc" \
-initrd /home/pjw/Downloads/busybox-1.34.1/myramdisk.gz
这里主要是加上了init ramdisk文件,并且修改了内核启动参数。
其他补充
x86在qemu中的模拟使用,更多的情况是类似于虚拟机的。qemu可以建立虚拟磁盘,给guest os使用, 另外还有网卡配置,图形界面配置,usb,cdrom,甚至快照功能等,和vmware虚拟机软件相似。