本文制作的磁盘镜像文件(*.img)用于在 Qemu virt 上启动 Linux
busybox 用于提供基础的系统命令,如 cat tree man dd chmod 等等
首先下载、配置、编译 busybox
1.1 下载并解压 BusyBox
下载最新版本的 BusyBox 源码:
bash
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xjf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
1.2 配置 BusyBox (静态编译)
这是最关键的一步。我们需要静态编译 BusyBox,这样它就不依赖系统中的动态链接库(glibc),可以直接在我们的极简环境中运行。
-
运行配置菜单:
bashmake menuconfig -
进入设置:
Settings--->- 勾选
[*] Build static binary (no shared libs)
-
保存并退出。
1.3 编译与安装
这里需要注意是否需要交叉编译
bash
make -j$(nproc)
make install
编译完成后,生成的文件会在当前目录下的 _install 文件夹中。
bash
m@m-ThinkPad-T14-Gen-2i:~/chy/busybox-1.36.1/_install$ ls
bin linuxrc sbin usr
此时 rootfs 目录结构初具雏形。
我们需要在 _install 的基础上完善 Linux 运行所需的目录结构。
bash
cd _install
mkdir -p dev proc sys etc/init.d tmp var
Linux 内核启动后会执行第一个程序 PID1(默认顺序是 /sbin/init, /etc/init, /bin/init, /bin/sh)。在使用 busybox 构建出来的 rootfs 中,他会先找到 /sbin/init,/sbin/init 会读取配置文件 /etc/inittab,我们提供这个文件,让 /sbin/init fork 出 bash------PID2
bash
# 创建文件
sudo touch mnt/etc/inittab
# 写入配置
sudo sh -c 'cat <<EOF > mnt/etc/inittab
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mount -t sysfs sysfs /sys
::sysinit:/bin/mount -t devtmpfs devtmpfs /dev
::sysinit:/bin/echo "------------------------------"
::sysinit:/bin/echo " System Boot Initialized"
::sysinit:/bin/echo "------------------------------"
# 在主控制台上启动 Shell
# "console" 关键字会自动匹配内核启动参数中的 console=...
console::askfirst:-/bin/sh
# 处理重启和关机信号
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF'
制作分区磁盘镜像 (Partition Image)
这是本任务的核心部分。我们将创建一个文件,将其虚拟为磁盘,创建分区,格式化,并写入数据。
3.1 创建空镜像文件
我们将创建一个大小为 128 MB 的空镜像文件。
bash
cd .. # 回到上级目录
dd if=/dev/zero of=disk.img bs=1M count=128
这部分为镜像文件创建了分区表和分区,其实这一步是不必要的。创建分区表和分区后,这个镜像文件应该称为磁盘镜像(它的第 00 个字节是分区表(MBR 或 GPT)和引导代码。文件系统并不在开头,而是被埋在文件内部的某个位置(通常是第 20482048 个扇区之后)。 ),对他的挂载会稍微复杂一点,但他在实际使用时的适配性会好一点。如果不创建分区和分区表,那整个镜像文件就是分区镜像(它的第 00 个字节开始就是文件系统(如 ext4, FAT32)的超级块(Superblock)。),可以直接挂载,但在一些场景下不好用。
3.2 创建分区表
使用 fdisk 创建一个主分区。
bash
# 通过管道自动输入命令:n(新建), p(主分区), 1(编号), 回车(默认起始), 回车(默认结束), w(写入)
echo -e "n\np\n1\n\n\nw" | fdisk disk.img
3.3 格式化并挂载
由于我们要操作镜像文件内部的分区,需要使用 losetup 将其映射为回环设备。
bash
# 将镜像映射到 loop 设备,-P 参数会自动扫描分区
sudo losetup -P -f --show disk.img
假设输出的设备是 /dev/loop0(请根据实际输出调整),那么分区就是 /dev/loop0p1。
格式化为 ext4:
bash
sudo mkfs.ext4 /dev/loop0p1
挂载分区:
bash
mkdir -p mnt sudo mount /dev/loop0p1 mnt
3.4 写入 RootFS
将之前 BusyBox 生成的 _install 目录下的所有内容复制到挂载点。
bash
sudo cp -r busybox-1.36.1/_install/* mnt/
现在 rootfs 中有 dev 目录,但其中还没有设备文件。虽然 /sbin/init 会重新 mount dev 目录,但是在那之前我们需要创建一些更基础的设备文件。
设备文件和普通文件的区别:普通文件和设备文件在磁盘中都对应一个 inode, 普通文件的 inode 中存储着一组指针,指向磁盘上具体的扇区,这些扇区里存储这文件的内容。设备文件的 inode 中不存储指向扇区的指针,而是存储着
设备类型:如 c 字符设备; b 块设备
主次设备号:主设备号即对应的驱动程序的 id, 次设备号即该驱动管理的具体设备编号
创建设备文件的方法就是把当前主机 rootfs 中的已有设备文件复制过来(这里的 -a 是必须的)
bash
# 直接从宿主机复制 console 和 null
sudo cp -a /dev/console mnt/dev
sudo cp -a /dev/null mnt/dev
卸载并断开连接:
bash
sudo umount mnt
sudo losetup -d /dev/loop0
现在,disk.img 就是一个包含完整 BusyBox 系统的可启动分区镜像了。