Linux 如何加载“initrd”映像?

Linux 如何加载“initrd”映像?

我一直在尝试了解启动过程,但只有一件事超出了我的想象......

一旦 Linux 内核启动并安装了根文件系统 (/),程序就可以运行,并且可以集成更多内核模块以提供附加功能。要挂载根文件系统,必须满足某些条件。内核需要相应的驱动程序来访问根文件系统所在的设备(特别是SCSI驱动程序)。内核还必须包含读取文件系统(ext2、reiserfs、romfs 等)所需的代码。也可以想象根文件系统已经被加密。在这种情况下,挂载文件系统需要密码。

初始 ramdisk(也称为 initdisk 或 initrd)正是解决了上述问题。 Linux 内核提供了一个选项,可以将小文件系统加载到 RAM 磁盘并在安装实际根文件系统之前在其中运行程序。initrd 的加载由引导加载程序(GRUB、LILO 等)处理。引导加载程序只需要 BIOS 例程即可从引导介质加载数据。如果引导加载程序能够加载内核,它还可以加载初始 ramdisk。不需要特殊的驱动程序。

如果 /boot 不是不同的分区,但存在于 / 分区中,那么引导加载程序不应该需要 SCSI 驱动程序来访问“initrd”映像和内核映像吗?如果您可以直接访问图像,那么为什么我们需要 SCSI 驱动程序?

答案1

Nighpher,我会尝试回答你的问题,但为了更全面地描述启动过程,请尝试这篇文章来自 IBM

好的,我假设您正在使用 GRUB 或 GRUB2 作为引导加载程序进行解释。首先,当 BIOS 访问磁盘以加载引导加载程序时,它会利用其内置例程进行磁盘访问,这些例程存储在著名的 13h 中断中。引导加载程序(以及设置阶段的内核)在访问磁盘时会使用这些例程。请注意,BIOS 在处理器的实模式(16 位模式)下运行,因此它无法寻址超过 2^20 字节的 RAM(2^20,而不是 2^16,因为实模式中的每个地址都由段地址* 组成) 16 + 偏移量,其中段地址和偏移量都是16位,参见维基百科上的“x86 内存分段”)。因此,这些例程无法访问超过 1 MiB 的 RAM,这是一个严格的限制,也是一个主要的不便。

BIOS 直接从 MBR(磁盘的前 512 个字节)加载引导加载程序代码并执行它。如果您使用 GRUB,则该代码是 GRUB stage 1。该代码加载 GRUB stage 1.5,该阶段位于磁盘空间的前 32 KiB(称为 DOS 兼容区域)中,或者来自文件系统的固定地址。执行此操作不需要了解文件系统结构,因为即使阶段 1.5 在文件系统中,它也是“原始”代码,可以直接加载到 RAM 并执行:请参阅 “PC 上 GRUB 的详细信息”,位于 Pixelbeat.org,这是下图的来源。第 1.5 阶段从磁盘到 RAM 的加载利用了 BIOS 磁盘访问例程。

在此输入图像描述

Stage 1.5包含文件系统实用程序,因此它可以从文件系统读取stage 2(好吧,它仍然使用BIOS 13h从磁盘读取到RAM,但现在它可以破译有关inode等的文件系统信息,并获取原始代码出磁盘)。由于磁盘寻址模式的限制,较旧的 BIOS 可能无法访问整个 HD – 它们可能使用柱头扇区系统,无法寻址超过前 8 GiB 的磁盘空间:http://en.wikipedia.org/wiki/Cylinder-head-sector

第二阶段加载内核写入 RAM(再次使用 BIOS 磁盘实用程序)。如果是2.6+内核,它也编译了initramfs,所以不需要加载它。如果它是较旧的内核,引导加载程序还会将独立的 initrd 映像加载到内存中,以便内核可以挂载它并获取用于从磁盘挂载真实文件系统的驱动程序。

问题是内核(和 ramdisk)的重量超过 1 MiB;因此,要将它们加载到 RAM 中,您必须将内核加载到前 1 MiB 中,然后跳转到保护模式(32 位),将加载的内核移动到高内存(释放实模式下的前 1 MiB),然后返回再次切换到真实(16 位)模式,将 ramdisk 从磁盘获取到前 1 MiB(如果它是单独的 initrd 和较旧的内核),可能会再次切换到受保护(32 位)模式,将其放到它所属的位置,可能会得到返回实模式(或不:https://stackoverflow.com/questions/4821911/does-grub-switch-to-protected-mode)并执行内核代码。警告:我不完全确定这部分描述的完整性和准确性。

现在,当你最终运行内核时,你已经拥有了它并且 ramdisk 通过引导加载程序加载到 RAM 中,因此内核可以使用 ramdisk 中的磁盘实用程序来安装真正的根文件系统并将 root 转移到它。 ramfs 驱动程序存在于内核中,因此它当然可以理解 initramfs 的内容。

答案2

我相信,这可以归结为特定引导加载程序支持哪些功能。例如。它不必知道组合(引导+根)分区的特定文件系统。在这种情况下,您只需创建一个单独的引导分区,以便它与您的引导加载程序一起工作,并且如何安装根分区的任何其他复杂性都保留在从引导分区引导的内核和 initrd 映像上。引导加载程序确实知道如何通过使用其自己的驱动程序或利用 BIOS 例程来访问 SCSI 设备(以及其他设备,具体取决于使用的引导加载程序)。此外,它还知道如何读取一些文件系统等。

考虑例如。 UEFI 启动方式,实际上 UEFI 固件已经知道如何访问 EFI 分区、读取它并从那里加载 Linux 内核,而不需要中间引导加载程序。在这种情况下,Linux 映像与根分区分开,并且 UEFI 固件不必知道所有外来文件系统即可访问它。我相信将“启动”映像与“根”分区分开很有意义。如果没有其他原因,那么在设置根文件系统加密时这是必要的。

答案3

只是为了记录,如果引导加载程序这样做不是加载 initrd 值得测试另一个引导加载程序;我刚刚遇到了这样的情况当 LILO 默默地忽略正确指定的中等大小的 initrd(<4Mb;SATA SSD 上的单个 ext4 rootfs;GPT)并且 GRUB 2.00 成功时。

启动过程很快结束,并出现典型的

RAMDISK: Couldn't find valid RAM disk image starting at 0.
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,3)

相关内容