BLOG

Record, summarize, and improve.

initrd & initramfs

Dentry(和 inode)缓存

Linux 中的文件系统子系统分为三层。VFS(虚拟文件系统),它实现系统调用接口并处理交叉安装点以及默认权限和限制检查。下面是各个文件系统的驱动程序以及与块设备(磁盘、存储卡等;网络接口除外)驱动程序的接口。

VFS 和文件系统之间的接口是几个类(它是普通的 C,因此包含指向函数等的指针,但从概念上讲它是面向对象的接口)。主要的三个类是inode,它描述文件系统中的任何对象(文件或目录),dentry,它描述目录中的条目,以及file,它描述进程打开的文件。安装后,文件系统驱动程序会为其根创建 并在进程想要访问文件并最终过期时按需创建其他文件系统驱动程序inodedentry这是一个 dentry 和 inode 缓存。

是的,这确实意味着对于每个打开的文件和任何到根目录的目录,都必须在内核内存中分配代表它的结构inodedentry

page cache

在Linux中,包含用户态数据的每个内存页都由统一的结构表示page。这可能会将页面标记为匿名(如果可用,可能会交换到交换空间)或将其与inode某些文件系统相关联(可能会写回文件系统并从文件系统重新读取),并且它可以是任意数量的内存映射的一部分,即在某个进程的地址空间中可见。当前加载到内存中的所有页面的总和就是页面缓存。

页面用于实现 mmap 接口,虽然文件系统可以通过其他方式实现常规读写系统调用,但大多数接口使用也使用页面的通用函数。有一些通用函数,当请求文件读取时分配页面并调用文件系统将它们一一填充。对于基于块设备的文件系统,它只是计算适当的地址并将此填充委托给块设备驱动程序。

ramdev(虚拟磁盘)

Ramdev 是常规块设备。这允许在其之上分层任何文件系统,但它受到块设备接口的限制。它只有填充调用者分配的页面并将其写回的方法。这正是磁盘、存储卡、USB 海量存储等实际块设备所需要的,但对于 ramdisk 来说,这意味着数据在内存中存在两次,一次在 ramdev 的内存中,一次在由 ramdev 分配的内存中。呼叫者。

这是旧的实现方式initrd。从那时起,initrd 就变得罕见且奇异了。

tmpfs

tmpfs 则不同。这是一个虚拟文件系统。它为 VFS 提供的方法是使其工作的绝对最低限度的方法(因此它是 inode、dentry 和文件方法应该做什么的优秀文档)。仅当 inode 缓存中有相应的 inode 和 dentry 时,文件才存在,在创建文件时创建,并且永远不会过期,除非文件被删除。当写入数据时,页面与文件相关联,否则表现为匿名页面(数据可以存储以交换,page只要文件存在,结构就保持使用)。

这意味着内存中没有额外的数据副本,整个过程变得更加简单,因此速度也稍快一些。它只是使用数据结构,作为任何其他文件系统的缓存,作为它的主存储。

这是新的实现方式initrdinitramfs,但图像仍称为 just initrd)。

这也是实现“posix共享内存”的方式(这简单地意味着安装了tmpfs,/dev/shm应用程序可以自由地在其中创建文件并mmap它们;简单而高效),并且最近甚至和/tmp/run/var/run)经常安装tmpfs,特别是在笔记本上防止磁盘旋转或避免 SSD 发生磨损。

initrd

  • ramdev创建块设备。它是一种基于 RAM 的块设备,即使用内存而不是物理磁盘的模拟硬盘。
  • initrdzcat initrd | dd of=/dev/ram0文件被读取并解压缩到设备中,就像您所做的那样或类似的事情。
  • 其中initrdmount /dev/ram0 /root包含文件系统的映像,因此现在您可以像往常一样挂载文件系统:。当然,文件系统需要驱动程序,因此如果您使用 ext2,则必须在内核中编译 ext2 驱动程序。
  • 完毕!

我们来看看initrd启动过程的更详细的解释:

  • 引导加载程序加载内核和初始 RAM 磁盘。
  • 内核将initrd此外,它还会清除initrd转换为 RAM 磁盘。正在使用的内存 。
  • 内核在/dev/ram0change_root文件路径中搜索根设备,如果没有找到,内核将执行过程。
  • 如果在/dev/ram0目录中找到,则将其挂载为根文件系统。
  • Init 加载真实文件系统并使用pivot_root系统调用将根文件系统移动到该目录。
  • Init在新创建的根文件系统上执行/sbin/init ,执行通常的引导顺序。
  • 引导加载程序断开 initrd文件系统的连接。

注意:更改根目录并不一定涉及卸载它。这使得我们可以在整个过程中让进程在initrd上运行。而且,我们可以轻松访问initrd下挂载的文件系统。

让我们创建一个新目录并从/boot/复制initrd.img文件:

$ mkdir initrd_test
$ cd initrd_test
$ cp /boot/initrd.img .
$ ls
initrd.img复制

有些系统(例如 Debian)要求我们首先提取initrd文件。让我们将文件重命名为.gz扩展名,以便我们可以提取它。此外,让我们使用file来确保解压后的文件实际上是 CPID 格式:

$ mv initrd.img initrd.img.gz
$ gunzip initrd.img.gz
$ file initrd.img
initrd.img: ASCII cpio archive (SVR4 with no CRC)复制

让我们使用cpio命令来提取图像:

$ cpio -id < initrd.img
62 blocks
$ ls
 initrd.img kernel复制

我们在 Linux Ubuntu 操作系统上提取了initrd.img文件的内容。解压后得到的文件数量可能会因我们的 Linux 发行版而异。

server ROM — bios — grub2 — kernel and initramfs/initrd — root

嵌入式linux ROM — BL0 — uboot — kernel — root

initramfs文件系统

Linux内核加载根文件系统执行/sbin/init程序前,需要找到根设备位置,如果根设需要驱动(例如SCSI设备等复杂驱动)的支持,内核有可能无能为力,通过提供一个过渡的临时根文件系统可以使得内核的设计更灵活简单.通常过渡的临时根文件系统基本上是内存文件系统.

常见的内存文件系统有:

rootfs,ramfs,ramdisk,tmpfs

1. rootfs:内核启动的初始始根文件系统,大部分linux系统正常运行后都会安装另外的文件系统,然后忽略rootfs

2. ramfs:基于内存的文件系统.ramfs文件系统没有容量大小的限制,它可以根据需要动态增加容量.直接利用了内核的磁盘高速缓存

3. ramdisk:基于ram的块设备,占据一块固定的内存,事先要使用特定的工具比如mke2fs格式化,还需要一个文件系统驱动来读写其上的文件空间固定导致容量有限,要想装入更多的文件需要重新格式化.Linux的块设备缓冲特性, ram disk上的数据被拷贝到page cache(对于文件数据)和dentry cache(对于目录项),导致内存浪费,它可能不停的动态增长直到耗尽系统的全部内存,所以只有root或授权用户允许使用ramfs

4. tmpfs:增加了容量大小的限制 + 允许把数据写入交换分区.由于增加了这两个特性,tmpfs允许普通用户使用

过渡根文件系统机制:将加载真正的根文件系统需要的设备驱动、工具以及初始化程序先加载到内存运行.

1. initrd: initial Ramdisk

/linuxrc文件,基于ramdisk技术,文件系统(ext2等)镜像文件 ————> cpio格式镜像文件

在内核启动完成后把它复制到/dev/ram块设备中, 作为内核加载真正根文件系统的过渡根文件系统

2. initramfs: initial RAM file system

init文件,cpio格式镜像文件

在内核启动完成后把它复制到rootfs中,作为内核初始的根文件系统,完成挂载系统真正的根文件系统

initramfs

  • A tmpfs mount -t tmpfs nodev /root已安装:. tmpfs 不需要驱动程序,它始终位于内核中。无需任何设备,无需额外的驱动程序。
  • initramfszcat initramfs | cpio -i直接解压缩到这个新文件系统:或类似文件系统中。
  • 完毕!

在启动过程中,内核遵循以下过程:

  • 如果我们在initramfs启动期间存在压缩的cpio存档,它将提取该存档并将微码数据从其中加载到 CPU。
  • 如果在initramfs启动期间存在未压缩的cpio存档,它将跳过该存档并将文件的其余部分设置为基本initramfs。相反,它将整个initramfs视为基本initramfs
  • 它将基本的initramfs cpio解压到基于 RAM 的磁盘中,并将其视为压缩的(Debian 上为 gzip,Ubuntu 上为 LZ4)存档文件。

从那里开始,大部分内核初始化和引导代码将被移动到该磁盘中并在用户模式下运行。其他类似地从内核移出的进程包括:

  • 启动时网络设置
  • 找到真正的根磁盘
  • ACPI 设置
  • initrd类型 RAM 磁盘的处理

优点

早期启动过程的定制变得更加容易。我们可以通过用户空间代码来实现这一点,这样就不需要修补内核来改变系统的启动方式。

将初始化代码移至用户空间可以更轻松地编写代码。

我们需要用户空间代码通过系统调用来处理内核,从而产生更干净、更安全的代码。

initramfs -tools-core为我们提供了lsinitramfs  ,我们可以用它来列出initramfs 内的文件。

我们还可以使用unmkinitramfs从initramfs中提取文件。

假设path/to/initrd已经作为空目录存在,并且输出文件 /initrd.img是 ASCII cpio存档(没有 CRC 的 SVR4)。

我们将考虑在initrd映像中添加微代码。让我们提取微代码并读取 STDERR 显示:

$ cd path/to/initrd/
$ cpio -i < /initrd.img
62 blocks复制

我们可以通过以下方式提取基本的initrd

$ cd path/to/initrd/
$ ddif=/initrd.img of=initrd.img bs =512 skip=62
$ zcat initrd.img | cpio -i复制

注意:我们应该调整跳过值以匹配cpio STDERR 输出。

让我们检查微码cpio存档大小:

$ cpio -t </initrd.img >/dev/null

initrdinitramfs的区别

我们可以将initrd用于 Linux 内核 2.4 及更低版本。相反,initramfs适用于 2.6 及更高版本的内核。

将initrd编译到内核中至少需要一个文件系统驱动程序,这可以提高启动时的灵活性、简单性和内存效率。

使用initramfs,我们可以使用内核将提取到 tmpfs 的文件创建一个存档这使得文件访问速度非常快。