linux 如何知道 rootfs 在哪里?

linux 如何知道 rootfs 在哪里?

我试图了解 Linux 内核如何知道启动时所需的 rootfs 在哪里。

我读过这个文档:

https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt

一部分感兴趣的是:

所有 2.6 Linux 内核都包含一个 gzip 压缩的“cpio”格式存档,该存档在内核启动时被提取到 rootfs 中...如果在嵌入的 cpio 存档提取到其中后 rootfs 不包含 init 程序,则内核将失败用于定位和挂载根分区的旧代码

我们的内核是 4.X,但我猜这仍然适用?这听起来好像所有内核都有一个嵌入式“cpio”rootfs。

事实上,正如我们读到的那样:

2.6 内核构建过程始终创建一个 gzipped cpio 格式的 initramfs 存档并将其链接到生成的内核二进制文件中。默认情况下,此存档为空...配置选项 CONFIG_INITRAMFS_SOURCE ...可用于指定 initramfs 存档的源

这又提出了几个问题:

  1. 因此,如果我希望我的 rootfs 位于 RAM 中,我需要设置CONFIG_INITRAMFS_SOURCE为指向我的 rootfs(大概是 cpio 格式)。

但这是否意味着我的内核和 rootfs 现在密不可分了?如果我想在不重建的情况下对 RootFS 进行一些小调整怎么办?如果我希望我的 rootfs 与内核分开存储怎么办?如何告诉内核我的 rootfs 的位置?

  1. 此外,如果我希望我的 rootfs 位于物理存储(如 eMMC、闪存驱动器等)而不是 RAM 中怎么办?

早些时候曾说过:

如果在嵌入式 cpio 存档解压到 rootfs 后,rootfs 不包含 init 程序,则内核将使用旧代码来定位并挂载根分区

但是……怎么办?它如何知道 rootfs 的位置?如果它在 eMMC 上,我需要以某种方式告诉内核,对吧?

我使用的引导程序是U-boot。我检查了 U-boot 环境变量,看看它是否以某种方式将 rootfs 位置作为启动参数传递给内核,但似乎并非如此......

编辑:

正如评论中指出的,rootfs 的位置通过 boot arg 传递给内核。就我而言,u-boot 作为启动参数传递root=/dev/mmcblk0p4 rw给内核。这样就回答了我的问题之一 - 您可以将位置作为启动参数传递给任何解压缩的 rootfs。

我仍然不清楚如何,考虑到一些rootfs.tar.gz与内核分离的东西,如何告诉内核将其解压到 RAM 并将其用作 rootfs。也许这是不可能的,我只需要使用CONFIG_INITRAMFS_SOURCE?无论如何,我会阅读 4.X 文档。

答案1

首先,不要被内核文档中对“2.6”的引用吓到。当前的内核仍然是“2.6”系列的成员,但它们只是出于“营销目的”而进行了两轮重新编号(因此 2.6.40 变为 3.0,3.20 然后变为 4.0)。您的 4.19 内核通常标记为 2.6.79。

看起来这里对“rootfs”的含义有些混淆。 “rootfs”是内核内部使用的一种特殊的基于 RAM 的文件系统。它与通常安装在/run/dev/shm或有时等位置的“tmpfs”文件系统完全相同/tmp。 (好吧,除非“tmpfs”功能没有编译到内核中,在这种情况下,会使用名为“ramfs”的精简“tmpfs”。)这些文件系统仅存在于页面缓存中,根本没有支持设备ramfs,而 tmpfs 由交换(如果可用)支持。

因此,内核不需要担心“找到”“rootfs”,但它需要以某种方式填充它,因为整个页面缓存在启动时是空的。这就是“initramfs 文件”发挥作用的地方。这只是一个(压缩的)cpio存档(不是tar出于文档中提到的原因),它被内核解压到空的“rootfs”中。该存档可以通过CONFIG_INITRAMFS_SOURCE构建期间的设置直接嵌入到内核映像中,也可以由引导加载程序提供(这就是initrdGRUB 中的选项的作用)。该存档通常是使用用户空间工具创建的,例如dracut或 (令人困惑的)mkinitrd

如果 cpio 映像不可用或不包含可执行文件/init,内核将回退到另一种方法,在该方法中查看root=命令行参数,将其解释为真实根文件系统的位置,将其挂载/并直接执行init。然而,现在很少使用这种方法,因为它需要将所有必要的存储和文件系统驱动程序直接编译到内核中。在大多数系统中,该root=参数不被内核使用,而是由/initinitramfs 中处理。它/init(通常是systemdshell 脚本)负责加载所需的模块、安装真正的根文件系统并切换到它。

很久以前,使用了一种称为“initrd”的不同机制来代替现代的“initramfs”。 “initrd”(“init ramdisk”)是一个基于 RAM 的块设备,它使用真实的文件系统(例如 ext2)进行初始化,其映像的提供方式就像现代存档一样cpio。这就是为什么很多地方仍然使用“initrd”名称来指代这些早期启动的东西。

答案2

因此,如果我希望我的 rootfs 位于 RAM 中,我需要设置 CONFIG_INITRAMFS_SOURCE 以指向我的 rootfs(大概是 cpio 格式)。

那是这样做的方法,是的,但这不是仅有的方式。

CONFIG_INITRAMFS_SOURCE如果您有一个可配置为将内核和 initramfs 作为单独文件加载的引导加载程序,则在构建内核时无需使用。CONFIG_BLK_DEV_INITRD在内核配置中设置就足够了。 (在 initramfs 之前,有一个名为 的旧版本技术initrd,并且旧名称仍然出现在某些地方。)引导加载程序将加载 initramfs 文件,然后将有关其内存位置和大小的一些信息填充到一个数据结构中已加载的内核映像的具体位置。内核有内置例程,将使用该信息在系统 RAM 中查找 initramfs 并将其解压缩。

将 initramfs 作为单独的文件将使您可以更轻松地修改 initramfs 文件,并且如果您的引导加载程序可以接受用户的输入,则可以指定要加载的另一个 initramfs 文件,而不是引导时加载的常规文件。 (如果您尝试创建自定义的 initramfs 并犯了一些错误,那么这非常方便。去过那里,做到过。)

对于传统的基于 BIOS 的 x86 系统,您可以在以下位置找到有关这些详细信息的信息:(内核源代码)/Documentation/x86/boot.txt。基于 UEFI 的系统的做法略有不同(也在同一文件中进行了描述),而其他架构(例如 ARM)有自己的一组有关将信息从引导加载程序传递到内核的详细信息。

此外,如果我希望我的 rootfs 位于物理存储(如 eMMC、闪存驱动器等)而不是 RAM 中怎么办?

在常规非嵌入式系统中,initramfs 通常仅包含足够的功能来激活基本子系统。在普通 PC 中,这些驱动程序通常是键盘、显示器的驱动程序和根文件系统存储控制器的驱动程序,以及激活 LVM、磁盘加密和/或软件 RAID 等子系统所需的任何内核模块和工具,如果您使用这些功能。

一旦基本子系统处于活动状态并且根文件系统可访问,initramfs 通常会执行pivot_root(8)从 initramfs 切换到真正的根文件系统的操作。但是嵌入式系统,或者像这样的专门实用程序数据库管理网络,可以将它需要的所有内容打包到 initramfs 中,但从不执行该pivot_root操作。

通常,initramfs 中的脚本和/或工具将从内核命令行上的选项获取必要的信息来定位真正的根文件系统。但你不为此:使用自定义的 initramfs,您可以执行一些操作,例如如果在启动序列中的特定时间按下特定的键或鼠标按钮,则切换到不同的根文件系统。

对于复杂的存储配置(例如,在使用冗余多路径 SAN 存储的系统上,软件 RAID 之上的加密 LVM),激活根文件系统所需的所有信息可能不适合内核命令行,因此您可以包含更大的部分放入 initramfs 中。


现代发行版通常使用initramfs 生成器为每个已安装的内核构建定制的 initramfs。不同的发行版曾经有自己的 initramfs 生成器:RedHat 使用,mkinitrd而 Debian 有update-initramfs.但在引入systemd它之后,看起来许多发行版都在标准化dracut作为 initramfs 生成器。

现代 initramfs 文件可以是多个存档的串联.cpio,每个部分可能会或可能不会被压缩。现代 x86_64 系统上的典型 initramfs 文件可能有一个“早期微代码更新”文件作为第一个组件(通常只是未压缩的 cpio 存档中的单个文件,因为微代码文件通常是加密的,因此不太可压缩。之后是常规 initramfs 内容,作为压缩.cpio文件。

为了更深入地了解您的系统,我鼓励您将 initramfs 文件提取到临时目录,然后检查其内容。在 Debian 上,有一个unmkinitramfs(8)工具可以用来以简单的方式提取 initramfs 文件。在 RedHat 7 上,您可能需要使用/usr/lib/dracut/skipcpio <initramfs file>跳过微码更新文件,然后通过管道将结果输出传递至gzcat并继续cpio -i -d将 initramfs 内容提取到当前工作目录。 Ubuntu 可能会使用lzcat代替gzcat.

相关内容