写在前面:
本篇是完整的在Linux环境下,创建一个虚拟磁盘,并且将内核安装到磁盘并用虚拟机运行的过程。需要对bootstrap过程有一定的基础知识。遇到不懂的概念,如MBR,Grub等请务必参考笔者的另一篇文章操作系统架构- Linux主机从按下电源键到加载内核,都经历了什么?(超详细版)此外,读者还需要对文件系统有基本的理解。
1. 创建磁盘镜像
1)通过在 Shell 提示符下输入以下命令创建磁盘镜像(提示符以 $
表示)
注意:以下示例创建一个 30MB 的磁盘镜像,设置为虚拟磁盘,具有 60 个柱面、16 个磁头和每轨道 63 个扇区。可以根据需要调整配置。每个扇区大小为 512 字节,共有 60*16*63
个扇区。
$ dd if=/dev/zero of=disk.img bs=512 count=60480
参数解释:
dd
是一个低级字节拷贝工具,用于创建一个虚拟磁盘文件。if=/dev/zero
:从设备/dev/zero
读取数据。/dev/zero
是 Linux 中的一个特殊设备文件,提供无穷的零字节流,保证磁盘文件为空。of=disk.img
:输出到文件disk.img
,即创建的磁盘镜像文件。bs=512
:每次写入的块大小是 512 字节(等于硬盘扇区的大小)。count=60480
:创建 60480 个扇区,总大小为60480 * 512 = 30MB
。
2.用fdisk在虚拟磁盘中设置分区表
(示例中创建一个主分区,用于引导内核镜像,并将其类型设置为 Linux ext2/ext3 原生文件系统)。
$ fdisk disk.img
使用 fdisk
工具在磁盘镜像中创建分区表。fdisk
的操作实际上就是修改分区表中对应分区条目的内容。分区表定义了磁盘的逻辑布局,决定了如何划分磁盘空间。(详细请参考MBR的partion table)
Command (m for help): x
x
进入专家模式,允许我们手动修改磁盘的几何参数(柱面、磁头和扇区)
Expert command (m for help): h
Number of heads (1-256, default 255): 16
Expert command (m for help): s
Number of sectors (1-63, default 63): 63
Expert command (m for help): c
Number of cylinders (1-1048576): 60
设置磁头数为 16(一个磁盘柱面中有 16 个磁头),每个磁头下有 63 个扇区,磁盘有 60 个柱面。计算磁盘大小: 磁盘总容量 = 柱面数 × 磁头数 × 每磁头扇区数 × 每扇区字节数
60×16×63×512≈30MB
Expert command (m for help): r
设置好磁盘参数后,输入上面的指令r返回普通分区模式,然后创建主分区:
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-60, default 1):
Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-60, default 60):
Using default value 60
创建一个主分区,并且设置分区编号为1。然后会提示输入起始和结束柱面,直接回车选择默认值。这个分区覆盖整个磁盘(从柱面 1 到柱面 60)
Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 83
将分区类型设置为 0x83
,即 Linux 原生文件系统(如 ext2/ext3)
Command (m for help): a
Partition number (1-4): 1
设置刚才创建的分区1为可引导分区。输入 a
,表示用户选择了设置或取消分区的引导标志(bootable flag)。(其实就是修改分区 1 的分区表条目(partition entry)中的标识符字段为 0x80)
引导标志用于指示某个分区是可引导的(即启动时 BIOS 会将该分区作为引导设备)。
Command (m for help): w
保存并且退出fdisk。
完成后,fdisk
会尝试更新分区表。如果失败(例如内核未加载新分区表),会提示警告:
WARNING: Re-reading the partition table failed...
这通常不会影响后续操作,新分区表会在下一次重启时生效。
3. 创建文件系统
首先确保回环设备模块已加载:
$ modprobe loop
如果出现错误(FATAL: Module loop not found
),说明该模块已经内置到内核中,可以跳过此命令。
然后需要找到分区的偏移量:
$ fdisk -u disk.img
使用 fdisk
查看磁盘镜像的分区表,选择 p
打印分区信息:
Disk disk.img: 30 MB, 31457280 bytes
16 heads, 63 sectors/track, 60 cylinders, total 61440 sectors
Units = sectors of 1 * 512 = 512 bytes
Device Boot Start End Blocks Id System
disk.img1 * 63 60479 30208+ 83 Linux
找到分区起始位置(即 Start
列的值),它是以扇区为单位的偏移量。计算字节偏移量: 字节偏移量=Start值×每扇区字节数。示例中 Start=63
,每扇区 512 字节,因此: 字节偏移量=63×512=32256。完成分区信息查看后,输入 q
退出 fdisk。
注:在早期的硬盘中,63 是每磁道扇区数的默认值(柱面-磁头-扇区模型,CHS),因此从第 63 扇区开始分区可以避免覆盖 MBR 或其他引导数据。起始扇区从 63 开始对齐时,正好位于第二个磁道的开头,这样可以避免交叉访问磁道,从而提升性能。对于现代存储设备(如 SSD),起始扇区通常会从 2048(1MB 对齐)开始以适应较大的块大小。
$ losetup -o 32256 /dev/loop0 disk.img
然后按上面的代码关联分区。losetup
命令用于将一个普通文件(如磁盘镜像文件)关联到一个回环设备(如 /dev/loop0
),从而使该文件看起来像一个块设备,便于进一步操作。这里是从磁盘镜像文件 /path/to/disk.img
的偏移量 32256 字节 开始,将其绑定到回环设备 /dev/loop0
。绑定后,/dev/loop0
会代表镜像文件的一个特定分区,操作 /dev/loop0
相当于操作该分区。
注:回环设备是一个虚拟的设备,专门用来对文件进行类似磁盘的操作。简单来说,它将文件“伪装”成设备,然后操作系统通过回环设备,使用标准的块设备接口(如读写操作)与文件交互。
$ mke2fs /dev/loop0
然后按照上面的代码,在 /dev/loop0
上创建 ext2 文件系统。操作系统通过 /dev/loop0
接口定位到磁盘镜像文件的对应分区(从偏移量 32256 开始)。mke2fs
在这个分区上创建 ext2 文件系统,格式化后可以用来存储文件。
注:因为镜像文件本身可能包含多个分区(由分区表决定)。所以必须用 losetup
将分区绑定到回环设备。然后对这个“分区设备”(即 /dev/loop0
)创建文件系统。在挂载之前,我们的磁盘镜像没有文件系统(只是一个空的二进制文件或原始分区数据),因此需要先通过 losetup
绑定到回环设备,再用工具(如 mke2fs
)创建文件系统。
$ losetup -d /dev/loop0
设置完成后,运行上面命令卸载(释放)loop 设备。卸载回环设备可以确保所有数据写入都完成。如果之后需要重新操作同一个磁盘镜像文件,可以再次绑定回环设备,重新分配资源。如果不卸载直接重新绑定,可能会导致系统冲突或设备忙(device is busy
)错误。
4. 挂载镜像并复制文件
这一步的目的是将虚拟磁盘的分区挂载到主机操作系统上,使其像普通文件系统一样操作。
首先加载文件系统模块:
$ modprobe ext2
即,使用 modprobe ext2
命令加载 ext2 文件系统模块。如果系统提示 FATAL: Module ext2 not found
错误,表示文件系统模块已经被编译进了内核,无需额外加载。在现代 Linux 系统中,ext2 支持通常是内置的,特别是在简化发行版(如 Puppy Linux)中,因此可能会跳过此步骤。
然后创建挂载点目录:
$ mkdir /mnt/C
$ mount disk.img /mnt/C -t ext2 -o loop,offset=32256
挂载磁盘分区到 /mnt/C。
挂载(Mount)是将一个文件系统整合到操作系统的目录结构中的过程。挂载完成后,文件系统内容就可以像普通文件夹一样访问。将磁盘镜像挂载到 /mnt/C
后,访问 /mnt/C
就相当于访问磁盘镜像中的文件内容。使用 mount
命令挂载磁盘镜像,指定文件系统类型为 ext2(
必须明确告诉操作系统磁盘镜像的文件系统类型,否则系统无法正确解析磁盘内容。)
,并使用回环设备模式挂载磁盘镜像的分区(回环设备将该文件模拟成一个磁盘分区),设置偏移量(offset
)为分区起始地址。
为什么需要挂载:
disk.img
是一个 虚拟磁盘镜像文件,它模拟了一个真实磁盘的结构,包括:分区表(Partition Table)分区(Partitions)文件系统(File System)它本质上是一个普通的文件,但内容被设计为符合磁盘的格式,和电脑上的真实硬盘不同,它不能被操作系统直接识别和使用。真实磁盘(如电脑硬盘或 U 盘)连接到电脑时,操作系统可以自动识别它们,并将其挂载到系统中,比如自动在 /media
或 D:/
显示磁盘内容。虚拟磁盘镜像文件(disk.img
)是一个普通文件,操作系统无法自动知道如何解析它。通过挂载,操作系统会将 disk.img
看作一个磁盘。找到它的文件系统内容(比如 ext2),并且将这些内容映射到一个挂载点目录。指令里-o loop
参数的作用是创建一个临时的回环设备,将普通文件视为块设备。
5. 在磁盘镜像上安装 GRUB 引导程序
Grub作为引导加载程序,用于加载后续的内核镜像。
我们将引导加载程序的所有处理阶段文件复制到虚拟磁盘的适当子目录,然后通过 GRUB Shell 配置引导加载程序脚本。
$ mkdir /mnt/C/boot
$ mkdir /mnt/C/boot/grub
$ cp /boot/grub/{stage1,e2fs_stage1_5,stage2} /mnt/C/boot/grub
$ grub
以上将 GRUB 的引导阶段文件 stage1
、e2fs_stage1_5
和 stage2
复制到虚拟磁盘的 grub
目录中。
然后在本机用启动 GRUB 的交互式 Shell:
$ grub
GRUB Shell 里运行的命令主要是为 stage2
服务的。这些命令将磁盘镜像文件 /path/to/disk.img
关联为 GRUB 的设备 (hd0)
,设置设备 (hd0)
的几何参数,设置 GRUB 根设备为第一个分区 (hd0,0),
最后安装 GRUB 引导加载程序到设备 (hd0)
。这些 GRUB 命令的核心目标就是在安装阶段生成一个完整的引导配置,并通过 setup
命令完成设置,并将 GRUB 的 stage1
引导代码写入 MBR
grub> device (hd0) disk.img
grub> geometry (hd0) 60 16 63
grub> root (hd0,0)
grub> setup (hd0)
执行完上述命令后,输入quit退出grub shell。
以下是一些细节解释:
GRUB 使用 (hdX,Y)
的形式来标识磁盘和分区。
(hd0)
表示第一个磁盘。(hd1)
表示第二个磁盘,以此类推。(hd0,0)
表示第一个磁盘的第一个分区。
在实际硬件中,(hd0)
通常是主硬盘(如 /dev/sda
)当使用虚拟磁盘(如 disk.img
)时,GRUB 不知道 disk.img
是哪个磁盘。因此,需要通过device命令告诉 GRUB:把 disk.img
当成是 GRUB 的第一个磁盘 (hd0)。
GRUB 内部的后续操作(如 geometry
、setup
)会通过 (hd0)
来访问 disk.img
,而不是直接处理磁盘文件路径。
Grub不读取分区表中的几何信息,需要依赖用户手动提供磁盘的几何结构,即geometry (hd0) 60 16 63。如果几何参数错误:GRUB 可能会将引导文件(如 stage1
或内核)定位到错误的位置
root (hd0,0)告诉 GRUB 根设备(root
)是 第一个磁盘的第一个分区,即 (hd0,0)
。这段命令告诉 GRUB:去第一个磁盘的第一个分区中查找引导文件(例如 menu.lst
或 grub.cfg
)
6. 复制内核并创建菜单
$ cp my_kernel /mnt/C/boot/
$ nano /mnt/C/boot/grub/menu.lst
复制编译好的内核文件my_kernel到磁盘镜像。配置 GRUB 的启动菜单 menu.lst
。menu.lst
是 GRUB 的配置文件,用于定义启动菜单项和内核加载方式。
菜单示例:
title MemOS-2
root (hd0,0)
kernel /boot/my_kernel
title My Linux Kernel
: 定义引导菜单的标题为 "My Linux Kernel"。root (hd0,0)
: 指定 GRUB 根设备为(hd0,0)
,即虚拟磁盘的第一个分区。kernel /boot/my_kernel
: 告诉 GRUB 引导内核镜像文件/boot/my_kernel
。
注:为什么还要设置 root(hd0,0)?因为之前在 GRUB Shell 中设置 root (hd0,0)
仅影响当前 GRUB Shell 的工作环境,是安装引导加载程序时所引用的设备路径。退出 GRUB Shell 后,这些设置并不会被保存下来。现在root(hd0,0)是为了启动时GRUB定位到目标分区。
7.用QEMU虚拟机运行内核
现在可以使用虚拟磁盘文件在任意 PC 模拟器中运行,例如 QEMU。笔者在puppy linux环境下使用下面命令运行。读者请根据自己的linux配置运行虚拟机。
用虚拟机装载磁盘:
qemu-system-i386 -hda disk.img
此时会启动qemu的虚拟机服务端,需要另外打开一个客户端来查看;
./root/vnc/opt/Tigervnc/bin/vncviewer 127.0.0.1:5900
上图就是Grub成功读取到内核后的启动菜单画面。
标签:操作系统,GRUB,hd0,分区,磁盘镜像,内核,磁盘,disk From: https://blog.csdn.net/qq_44252574/article/details/143870401