据我所知,initrd
充当块设备,因此需要文件系统驱动程序(例如ext2
)。内核必须至少有一个内置模块,用于检测initrd
的文件系统。在本文Introducing initramfs, a new model for initial RAM disks中,写了:
但是由于缓存,ramdisks实际上浪费了更多的内存。 Linux是 旨在缓存从中读取或写入的所有文件和目录条目 阻止设备,因此Linux将数据复制到ramdisk和从ramdisk复制到 “页面缓存”(用于文件数据)和“dentry cache”(用于 目录条目)。假装是一个ramdisk的缺点 块设备被视为块设备。
page cache
和dentry cache
是什么?在段落中,是否意味着数据被复制,因为ramdisk
被视为块设备,因此所有数据都被缓存了?
相反,ramfs
:
几年前,Linus Torvalds有一个很好的想法:如果Linux的缓存怎么办? 可以像文件系统一样安装吗?只需将文件保存在缓存中即可 永远不会删除它们,直到它们被删除或系统重新启动? Linus在缓存周围写了一个名为“ramfs”的小包装器 内核开发人员创建了一个名为“tmpfs”的改进版本(其中 可以将数据写入交换空间,并限制给定挂载的大小 指出它在消耗所有可用内存之前填满了。 initramfs的 是tmpfs的一个实例。
这些基于ram的文件系统会自动增长或缩小以适应 它们包含的数据大小。将文件添加到ramfs(或扩展 现有文件)自动分配更多内存,并删除或 截断文件释放内存。两者之间没有重复 阻止设备和缓存,因为没有阻止设备。副本 缓存是数据的唯一副本。最重要的是,这不是新的 代码,但现有Linux缓存代码的新应用程序,其中 意味着它几乎没有增加尺寸,非常简单,并且基于 经过严格测试的基础设施。
总之,ramfs
只是文件打开并加载到内存中,不是吗?
initrd
和ramfs
都是在编译时压缩的,但不同的是,initrd
是一个在启动时由内核解压缩的块设备,而ramfs
通过cpio解压缩到内存中。我对么?或者ramfs
是一个非常小的文件系统?
最后,直到今天,initrd
图像仍然显示在最新的内核中。但是,initrd
实际上是今天使用的ramfs
,名称仅用于历史目的吗?
答案 0 :(得分:54)
我认为你是对的。
如果您按照启动时所需的步骤操作,则很容易看出差异:
initrd
ramdev
块设备。它是一个基于ram的块设备,它是一个使用内存而不是物理磁盘的模拟硬盘。initrd
文件并将其解压缩到设备中,就像您执行zcat initrd | dd of=/dev/ram0
或类似操作一样。 initrd
包含文件系统的图像,因此现在您可以像往常一样挂载文件系统:mount /dev/ram0 /root
。当然,文件系统需要一个驱动程序,所以如果使用ext2,则必须在内核中编译ext2驱动程序。initramfs
tmpfs
:mount -t tmpfs nodev /root
。 tmpfs不需要驱动程序,它总是在内核上。无需设备,无需额外的驱动程序。initramfs
直接解压缩到这个新文件系统中:zcat initramfs | cpio -i
或类似文件。是的,虽然它是initrd
,但它在许多地方仍称为initramfs
,特别是在引导加载程序中,因为它们只是一个BLOB。操作系统在启动时会产生差异。
答案 1 :(得分:44)
Linux中的文件系统子系统有三层。 VFS(虚拟文件系统),它实现系统调用接口并处理交叉挂载点以及默认权限和限制检查。下面是各个文件系统的驱动程序,然后是块设备驱动程序的接口(磁盘,存储卡等;网络接口是例外)。
VFS和文件系统之间的接口是几个类(它是普通的C,所以结构包含指向函数等的指针,但它在概念上是面向对象的接口)。主要的三个类是inode
,它描述了文件系统中的任何对象(文件或目录),dentry
描述了目录中的条目,file
描述了进程打开的文件。安装后,文件系统驱动程序为其根创建inode
和dentry
,并在进程想要访问文件并最终到期时按需创建其他文件系统驱动程序。这是一个dentry和inode缓存。
是的,它确实意味着对于每个打开的文件和根目录下的任何目录,必须在内核内存中分配inode
和dentry
结构来表示它。
在Linux中,包含用户态数据的每个内存页面都由统一的page
结构表示。这可能会将页面标记为匿名(如果可用,可能会交换到交换空间),或者在某些文件系统上将其与inode
关联(可能会写回文件系统并从文件系统重新读取),它可能是任意数量的内存映射,即在某些进程的地址空间中可见。当前在内存中加载的所有页面的总和是页面缓存。
这些页面用于实现mmap接口,而常规的读写系统调用可以通过其他方式由文件系统实现,大多数接口使用也使用页面的泛型函数。有一些通用函数,当请求文件读取时,分配页面并调用文件系统逐个填充它们。对于基于块设备的文件系统,它只计算适当的地址并将此填充委托给块设备驱动程序。
Ramdev是常规的阻止设备。这允许在其上层叠任何文件系统,但它受块设备接口的限制。而这只是填写调用者分配的页面并将其写回的方法。这正是真正的块设备所需要的,例如磁盘,存储卡,USB大容量存储等,但对于ramdisk而言,这意味着数据存在于内存中两次,一次存储在ramdev的内存中,一次存储在由ramdev分配的内存中。呼叫者。
这是实施initrd
的旧方法。从initrd罕见和充满异国情调的时候开始。
Tmpfs与众不同。这是一个虚拟文件系统。它为VFS提供的方法是使其工作的绝对最低限度(因此它是inode,dentry和file方法应该做的优秀文档)。只有在创建文件时创建并且永不过期的inode缓存中存在相应的inode和dentry时,文件才存在,除非删除该文件。页面在写入数据时与文件相关联,否则表现为匿名页面(数据可以存储为交换,只要文件存在,page
结构仍在使用中。)
这意味着内存中没有额外的数据副本,整个事情要简单得多,而且由于速度稍快一些。它只是使用数据结构,作为任何其他文件系统的缓存,因为它是主存储。
这是实施initrd
(initramfs
的新方法,但图片仍称为initrd
)。
这也是实现“posix共享内存”的方式(这意味着tmpfs安装在/dev/shm
上,应用程序可以自由地在那里创建文件并对其进行mmap;简单而有效)最近甚至{{1并且/tmp
(或/run
)通常会在笔记本电脑上安装tmpfs,以防止磁盘旋转或在SSD情况下避免磨损。
答案 2 :(得分:0)
最少的可运行QEMU示例和新手说明
在这个答案中,我将:
希望这些可以作为基础,以验证和理解差异的更多内部细节。
最小设置为fully automated here,这就是corresponding getting started。
安装程序会在运行时打印出QEMU命令,并且如该存储库中所述,我们可以轻松生成以下三种工作类型的引导程序:
根文件系统位于ext2“硬盘”中:
qemu-system-x86_64 -kernel normal/bzImage -drive file=rootfs.ext2
根文件系统位于initrd中:
qemu-system-x86_64 -kernel normal/bzImage -initrd rootfs.cpio
-drive
未给出。
rootfs.cpio
包含与rootfs.ext2
相同的文件,不同之处在于它们的格式为CPIO,类似于.tar
:它会序列化目录而不压缩它们。
根文件系统位于initramfs中:
qemu-system-x86_64 -kernel with_initramfs/bzImage
-drive
和-initrd
均未给出。
with_initramfs/bzImage
是一个内核,编译后的选项与normal/bzImage
相同,除了以下一项:CONFIG_INITRAMFS_SOURCE=rootfs.cpio
指向与-initrd
示例完全相同的CPIO。
通过比较设置,我们可以得出每个设置的最基本属性:
在硬盘设置中,QEMU将bzImage加载到内存中。
这项工作通常由引导加载程序/固件在真实硬件such as GRUB中完成。
Linux内核启动,然后使用其驱动程序从磁盘读取根文件系统。
在initrd设置中,QEMU除了将内核加载到内存中以外,还进行了一些进一步的引导加载程序工作:
rootfs.cpio
加载到内存中这一次,由于不存在硬盘,因此内核仅直接使用内存中的rootfs.cpio
。
由于所有内容都在内存中,因此写入不会在重新启动后永久存在
在initramfs设置中,我们对内核的构建略有不同:我们还将rootfs.cpio
赋予内核构建系统。
然后,内核构建系统知道如何将内核映像和CPIO粘贴到一个映像中。
因此,我们要做的就是将bzImage传递给QEMU。 QEMU像其他设置一样将其加载到映像中,但是不需要任何其他操作:由于CPIO被粘贴到内核映像上,因此它也被加载到内存中!
答案 3 :(得分:0)
要在initrd
和initramfs
之间添加另一个值得注意的区别,上面的出色答案中没有提到。
initrd
内核将移至pid 0
处的用户空间/sbin/init
pid 0
执行/init
因为它可能会成为一个陷阱(请参阅https://unix.stackexchange.com/a/147688/24394)