Gil*_*pie 3 linux-kernel root-filesystem
我试图了解 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 存档的源
这又引发了几个问题:
CONFIG_INITRAMFS_SOURCE指向我的 rootfs(大概是 cpio 格式)。但这不是意味着我的内核和 rootfs 现在是不可分割的吗?如果我想在不重建的情况下对 RootFS 进行小幅调整怎么办?如果我希望我的 rootfs 与内核分开存储怎么办?如何告诉内核我的 rootfs 的位置?
之前说过:
如果在嵌入的 cpio 归档文件被提取到 rootfs 之后,它不包含一个 init 程序,内核将使用旧代码来定位和挂载一个根分区
但是……怎么办?它如何知道在哪里可以找到 rootfs?如果它在 eMMC 上,我需要以某种方式告诉内核,对吗?
我使用的引导程序是 U-boot。我检查了 U-boot 环境变量,看看它是否以某种方式将 rootfs 位置作为引导参数传递给内核,但似乎并非如此......
编辑:
正如评论中所指出的,rootfs 的位置通过引导参数传递给内核。就我而言,u-bootroot=/dev/mmcblk0p4 rw作为引导参数传递给内核。这样就回答了我的一个问题 - 您可以将该位置作为引导参数传递给任何解压缩的 rootfs。
我仍然不清楚如何,考虑到一些rootfs.tar.gz与内核分离的内容,如何告诉内核将其解压缩到 RAM 中并将其用作 rootfs。也许这是不可能的,我只需要使用CONFIG_INITRAMFS_SOURCE? 无论如何,我会阅读 4.X 文档。
首先,不要被内核文档中对“2.6”的引用吓到。当前的内核仍然是“2.6”系列的成员,但它们为了“营销目的”经历了两轮重新编号(因此 2.6.40 变成了 3.0,3.20 然后变成了 4.0)。您的 4.19 内核通常标记为 2.6.79。
看起来这里对“rootfs”的含义有些困惑。“rootfs”是内核内部使用的一种特殊的基于 RAM 的文件系统。这是完全一样的“tmpfs的”即通常安装到的地方,如文件系统/run,/dev/shm或有时/tmp。(好吧,除非“tmpfs”功能没有被编译到内核中,在这种情况下,使用一个名为“ramfs”的精简“tmpfs”代替。)这些文件系统只存在于页面缓存中,根本没有支持设备ramfs,而 tmpfs 由交换支持(如果可用)。
因此内核不需要担心“找到”“rootfs”,但它需要以某种方式填充它,因为整个页面缓存在启动时是空的。这就是“initramfs 文件”发挥作用的地方。这只是一个(压缩的)cpio存档(不是tar出于文档中提到的原因),它被内核解压缩到空的“rootfs”中。该存档可以通过CONFIG_INITRAMFS_SOURCE在构建期间进行设置直接嵌入到内核映像中,也可以由引导加载程序提供(这就是initrdGRUB 中的选项所做的)。这个存档通常是使用用户空间工具创建的,比如dracut或 (令人困惑)mkinitrd。
如果 cpio 映像不可用或不包含可执行文件/init,内核会回退到不同的方法,它查看root=命令行参数,将其解释为真实根文件系统的位置,将其挂载/并init直接执行。但是,现在很少使用它,因为它需要将所有必要的存储和文件系统驱动程序直接编译到内核中。在大多数系统中,该root=参数不被内核使用,而是由/initin initramfs 处理。那/init(通常是systemd或 shell 脚本)负责加载所需的模块,安装真正的根文件系统,并切换到它。
很久以前,使用一种称为“initrd”的不同机制代替现代的“initramfs”。“initrd”(“init ramdisk”)是一个基于 RAM 的块设备,它使用真实的文件系统(例如 ext2)进行初始化,其映像就像现代cpio档案一样提供。这就是为什么许多地方仍然使用“initrd”名称来指代这些早期启动的东西。
因此,如果我希望我的 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 系统,您可以在(kernel source)/Documentation/x86/boot.txt中找到有关这些详细信息的信息。基于 UEFI 的系统的做法略有不同(也在同一文件中进行了描述),而其他架构(例如 ARM)有自己的一组有关将信息从引导加载程序传递到内核的详细信息。
此外,如果我希望我的 rootfs 位于物理存储(如 eMMC、闪存驱动器等)而不是 RAM 中怎么办?
在常规非嵌入式系统中,initramfs 通常仅包含足够的功能来激活基本子系统。在普通 PC 中,这些驱动程序通常是键盘、显示器的驱动程序和根文件系统存储控制器的驱动程序,以及激活 LVM、磁盘加密和/或软件 RAID 等子系统所需的任何内核模块和工具,如果您使用这些功能。
一旦基本子系统处于活动状态并且根文件系统可访问,initramfs 通常会执行pivot_root(8)从 initramfs 切换到真正的根文件系统的操作。但是嵌入式系统或像DBAN这样的专门实用程序可以将其所需的所有内容打包到 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.
| 归档时间: |
|
| 查看次数: |
7746 次 |
| 最近记录: |