Ramfsrootfs 和initramfs
Posted rtoax
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Ramfsrootfs 和initramfs相关的知识,希望对你有一定的参考价值。
Ramfs, rootfs and initramfs — The Linux Kernel documentationhttps://www.kernel.org/doc/html/latest/filesystems/ramfs-rootfs-initramfs.html?highlight=initramfs
什么是 ramfs?
Ramfs 是一个非常简单的文件系统,它将 Linux 的磁盘缓存机制(页面缓存和 dentry 缓存)导出为一个可动态调整大小的基于 RAM 的文件系统。
通常所有文件都被 Linux 缓存在内存中。从后备存储(通常是挂载文件系统的块设备)读取的数据页会被保留,以防再次需要它,但标记为干净(可释放)以防虚拟内存系统需要内存用于其他用途。类似地,写入文件的数据在写入后备存储后立即标记为干净,但保留用于缓存目的,直到 VM 重新分配内存。类似的机制(dentry 缓存)大大加快了对目录的访问速度。
使用 ramfs,没有后备存储。写入 ramfs 的文件像往常一样分配 dentry 和页面缓存,但无处可写。这意味着页面永远不会被标记为干净的,因此当 VM 寻求回收内存时,它们不能被释放。
实现 ramfs 所需的代码量很少,因为所有工作都由现有的 Linux 缓存基础设施完成。基本上,您将磁盘缓存安装为文件系统。因此,ramfs 不是可通过 menuconfig 移除的可选组件,因为节省的空间可以忽略不计。
ramfs 和 ramdisk:
较旧的“ram 磁盘”机制从 RAM 区域创建了一个合成块设备,并将其用作文件系统的后备存储。这个块设备是固定大小的,所以安装在它上面的文件系统是固定大小的。使用 ram 磁盘还需要将内存从伪造的块设备不必要地复制到页面缓存中(并将更改复制回来),以及创建和销毁 dentry。此外,它需要一个文件系统驱动程序(例如 ext2)来格式化和解释这些数据。
与 ramfs 相比,这会浪费内存(和内存总线带宽),为 CPU 创造不必要的工作,并污染 CPU 缓存。(有一些技巧可以通过使用页表来避免这种复制,但它们非常复杂,而且无论如何都与复制一样昂贵。)更重要的是,ramfs 所做的所有工作都必须_无论如何_ ,因为所有文件访问都通过页面和 dentry 缓存。RAM 磁盘根本没有必要;ramfs 在内部要简单得多。
ramdisks 半过时的另一个原因是环回设备的引入提供了一种更灵活和方便的方式来创建合成块设备,现在从文件而不是从内存块。有关详细信息,请参阅 Lostup (8)。
ramfs 和 tmpfs:
ramfs 的一个缺点是您可以继续向其中写入数据,直到填满所有内存,并且 VM 无法释放它,因为 VM 认为文件应该写入后备存储(而不是交换空间),但 ramfs 没有没有任何后备商店。因此,应该只允许 root(或受信任的用户)对 ramfs 挂载进行写访问。
创建了一个名为 tmpfs 的 ramfs 衍生物,以增加大小限制以及将数据写入交换空间的能力。可以允许普通用户对 tmpfs 挂载进行写访问。有关更多信息,请参阅Tmpfs。
什么是rootfs?
Rootfs 是 ramfs(或 tmpfs,如果已启用)的一个特殊实例,它始终存在于 2.6 系统中。您无法卸载 rootfs 的原因与您无法终止 init 进程的原因大致相同;与使用特殊代码来检查和处理空列表相比,内核确保某些列表不会变空更小更简单。
大多数系统只是在 rootfs 上挂载另一个文件系统并忽略它。一个空的 ramfs 实例占用的空间很小。
如果启用了 CONFIG_TMPFS,rootfs 将默认使用 tmpfs 而不是 ramfs。要强制使用 ramfs,请在内核命令行中添加“rootfstype=ramfs”。
什么是initramfs?
所有 2.6 Linux 内核都包含一个 gzip 压缩的“cpio”格式存档,当内核启动时,它会被提取到 rootfs 中。解压后,内核检查 rootfs 是否包含文件“init”,如果是,则以 PID 1 执行它。 如果找到,则此 init 进程负责使系统运行其余部分,包括定位和挂载真正的根设备(如果有的话)。如果在嵌入的 cpio 归档文件被提取到 rootfs 之后,它不包含一个 init 程序,内核将使用旧代码来定位和挂载一个根分区,然后执行 /sbin/init 的一些变体。
所有这些在几个方面与旧的 initrd 不同:
旧的 initrd 始终是一个单独的文件,而initramfs存档链接到 linux 内核映像。(该目录
linux-*/usr
专门用于在构建期间生成此存档。)旧的 initrd 文件是一个 gzipped 文件系统映像(某些文件格式,例如 ext2,需要内核内置驱动程序),而新的 initramfs存档是 gzipped cpio 存档(就像 tar 只是更简单,请参阅 cpio(1)和initramfs 缓冲区格式)。内核的cpio提取代码不仅极小,而且还有__init文本和在引导过程中可以丢弃的数据。
由旧的 initrd(称为 /initrd,而不是 /init)运行的程序做了一些设置然后返回到内核,而来自initramfs的 init 程序 预计不会返回到内核。(如果 /init 需要移交控制权,它可以使用新的根设备过载 / 并执行另一个 init 程序。请参阅下面的 switch_root 实用程序。)
当切换另一个根设备时,initrd 将 pivot_root 然后卸载 ramdisk。但是initramfs是 rootfs:您既不能 pivot_root rootfs,也不能卸载它。而是删除 rootfs 中的所有内容以释放空间(find -xdev / -exec rm '{}' ';'),使用新根目录(cd /newmount; mount –move . /; chroot .)重载 rootfs,附加stdin/stdout/stderr 到新的 /dev/console,然后执行新的 init。
由于这是一个非常挑剔的过程(并且涉及在您可以运行之前删除命令),因此 klibc 包引入了一个帮助程序 (utils/run_init.c) 来为您完成所有这些工作。大多数其他软件包(例如busybox)将此命令命名为“switch_root”。
填充initramfs的:
2.6 内核构建过程总是创建一个 gzipped cpio 格式的initramfs 存档并将其链接到生成的内核二进制文件中。默认情况下,此存档为空(在 x86 上消耗 134 字节)。
配置选项 CONFIG_ INITRAMFS _SOURCE(在 menuconfig 的 General Setup 中,位于 usr/Kconfig 中)可用于指定 initramfs 存档的源,它将自动合并到生成的二进制文件中。此选项可以指向现有的 gzip cpio 存档、包含要存档的文件的目录或文本文件规范,例如以下示例:
dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
nod /dev/loop0 644 0 0 b 7 0
dir /bin 755 1000 1000
slink /bin/sh busybox 777 0 0
file /bin/busybox initramfs/busybox 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
dir /mnt 755 0 0
file /init initramfs/init.sh 755 0 0
运行“usr/gen_init_cpio”(在内核构建之后)以获取记录上述文件格式的使用消息。
配置文件的优点之一是无需 root 访问权限即可在新存档中设置权限或创建设备节点。(请注意,这两个示例“文件”条目期望在 linux-2.6.* 目录下名为“ initramfs ”的目录中找到名为“init.sh”和“busybox”的文件。有关更多详细信息,请参阅 早期用户空间支持。)
内核不依赖于外部 cpio 工具。如果指定目录中的配置文件,而不是,内核的构建基础设施从目录中创建一个配置文件(USR / Makefile文件调用USR / gen_ initramfs的.SH),并进行使用配置文件(通过喂养它打包该目录到 usr/gen_init_cpio,它是从 usr/gen_init_cpio.c 创建的)。内核的构建时 cpio 创建代码是完全自包含的,并且内核的引导时提取器(显然)也是自包含的。
您可能需要安装外部 cpio 实用程序的一件事是创建或提取您自己准备好的 cpio 文件以提供给内核构建(而不是配置文件或目录)。
以下命令行可以将 cpio 映像(通过上述脚本或内核构建)提取回其组件文件:
cpio -i -d -H newc -F initramfs_data.cpio --no-absolute-filenames
以下 shell 脚本可以创建一个预构建的 cpio 存档,您可以使用它来代替上述配置文件:
#!/bin/sh
# Copyright 2006 Rob Landley <rob@landley.net> and TimeSys Corporation.
# Licensed under GPL version 2
if [ $# -ne 2 ]
then
echo "usage: mkinitramfs directory imagename.cpio.gz"
exit 1
fi
if [ -d "$1" ]
then
echo "creating $2 from $1"
(cd "$1"; find . | cpio -o -H newc | gzip) > "$2"
else
echo "First argument must be a directory"
exit 1
fi
笔记
cpio 手册页包含一些不好的建议, 如果您遵循这些建议,将会破坏您的initramfs存档。它说“生成文件名列表的典型方法是使用 find 命令;你应该给 find -depth 选项,以尽量减少不可写或不可搜索目录的权限问题。” 创建 initramfs.cpio.gz 图像时不要这样做,它不起作用。Linux 内核 cpio 提取器不会在不存在的目录中创建文件,因此目录条目必须位于进入这些目录的文件之前。上面的脚本以正确的顺序获取它们。
外部initramfs映像:
如果内核启用了 initrd 支持,也可以将外部 cpio.gz 归档文件传递到 2.6 内核中以代替 initrd。在这种情况下,内核将自动检测类型(initramfs,而不是 initrd ),并在尝试运行 /init 之前将外部 cpio 存档提取到 rootfs 中。
这具有initramfs(无 ramdisk 块设备)的内存效率优势,但 initrd 的单独包装(如果您有非 GPL 代码希望从 initramfs 运行,这很好,而不会将其与 GPL 许可的 Linux 内核二进制文件混为一谈)。
它还可用于补充内核的内置initramfs映像。外部存档中的文件将覆盖内置 initramfs 存档中的任何冲突文件。一些发行商还喜欢使用特定于任务的 initramfs 映像自定义单个内核映像,而无需重新编译。
initramfs 的内容:
一个initramfs的档案是用于Linux的完全独立的根文件系统。如果您还不了解启动和运行最小根文件系统所需的共享库、设备和路径,以下是一些参考资料:
“klibc”包 ( Index of /pub/linux/libs/klibc/ ) 被设计为一个微型 C 库,用于静态链接早期用户空间代码以及一些相关实用程序。它是 BSD 许可的。
我自己使用 uClibc ( https://www.uclibc.org ) 和 busybox ( https://www.busybox.net )。它们分别是 LGPL 和 GPL。(一个独立的initramfs 包计划用于 busybox 1.3 版本。)
从理论上讲,您可以使用 glibc,但这不太适合像这样的小型嵌入式用途。(一个与 glibc 静态链接的“hello world”程序超过 400k。使用 uClibc 是 7k。还要注意 glibc dlopens libnss 进行名称查找,即使在其他情况下静态链接也是如此。)
一个好的第一步是让initramfs作为 init 运行一个静态链接的“hello world”程序,并在像 qemu (www.qemu.org) 或用户模式 Linux 这样的模拟器下测试它,如下所示:
cat > hello.c << EOF
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
printf("Hello world!\\n");
sleep(999999999);
}
EOF
gcc -static hello.c -o init
echo init | cpio -o -H newc | gzip > test.cpio.gz
# Testing external initramfs using the initrd loading mechanism.
qemu -kernel /boot/vmlinuz -initrd test.cpio.gz /dev/zero
当调试一个普通的根文件系统时,能够用“init=/bin/sh”引导是很好的。该initramfs的相当于是“rdinit = / bin / sh的”,它只是作为有用的。
为什么是cpio而不是tar?
这个决定是在 2001 年 12 月做出的。讨论从这里开始:
并产生了第二个线程(特别是在 tar 与 cpio 上),从这里开始:
快速而肮脏的摘要版本(不能替代阅读上述线程)是:
-
cpio 是一个标准。它已经有几十年的历史(从 AT&T 时代开始),并且已经广泛用于 Linux(在 RPM 中,Red Hat 的设备驱动程序磁盘)。这是 1996 年关于它的 Linux Journal 文章:
它不如 tar 流行,因为传统的 cpio 命令行工具需要 _truly_hideous_ 命令行参数。但这并没有说明存档格式,还有其他工具,例如:
-
内核选择的 cpio 存档格式比任何(实际上是几十种)各种 tar 存档格式都更简单、更清晰(因此更容易创建和解析)。完整的initramfs归档格式在 buffer-format.txt 中有解释,在 usr/gen_init_cpio.c 中创建,在 init/initramfs.c 中提取。这三者加起来总共不到 26k 的人类可读文本。
-
对 tar 进行标准化的 GNU 项目与对 zip 进行标准化的 Windows 大致相同。Linux 不属于这两者,可以自由地做出自己的技术决策。
-
由于这是内核内部格式,因此很容易成为全新的东西。内核提供了自己的工具来创建和提取这种格式。使用现有标准是可取的,但不是必需的。
-
Al Viro 做出了决定(引用:“tar 非常丑陋,不会在内核方面得到支持”):
解释了他的理由:
最重要的是,设计并实现了initramfs代码。
未来发展方向:
今天(2.6.16),initramfs总是被编译进来,但并不总是被使用。只有当 initramfs 不包含 /init 程序时,内核才会回退到旧的引导代码。回退是遗留代码,以确保平稳过渡并允许早期启动功能逐渐转移到“早期用户空间”(IE initramfs)。
迁移到早期用户空间是必要的,因为查找和安装真正的根设备很复杂。根分区可以跨越多个设备(raid 或单独的日志)。它们可以在网络上(需要 dhcp、设置特定的 MAC 地址、登录服务器等)。它们可以存在于可移动媒体上,具有动态分配的主要/次要编号和持续的命名问题,需要完整的 udev 实现来解决。它们可以被压缩、加密、写时复制、环回挂载、奇怪的分区等等。
这种复杂性(不可避免地包括策略)在用户空间中得到了正确处理。klibc 和 busybox/uClibc 都在处理简单的initramfs 包以放入内核构建。
klibc 包现在已被 Andrew Morton 的 2.6.17-mm 树接受。内核当前的早期启动代码(分区检测等)可能会迁移到默认的initramfs 中,由内核构建自动创建和使用。
以上是关于Ramfsrootfs 和initramfs的主要内容,如果未能解决你的问题,请参考以下文章